[Intro] Hello Fellow Scratchers! Welcome
back to our RPG series – Our worlds are looking fantastic, but limited in size. In this episode I
will introduce the concept of saving and loading multiple scenes. This is the basis for creating
larger interconnected world maps, and for enter buildings! – A much anticipated feature I know.
So, the active scene is stored in list named GRID, containing around 30,000 tiles. This doesn’t want
to change as it is fast and efficient. To allow us to store many other scenes, we will introduce
two new lists; “Store Name” containing each scene’s name (e.g. “overworld”). And,
the list “Store Data” that will contain the scene itself. In computer science, this setup is called
a key-value pair, or a Data Dictionary, and is super common. So just like how in Scratch we can
switch sprite costumes by name, so we will be able to switch to an RPG scene by name too. Very cool.
But before we can do this, we need a way of storing a Scene (all
30,000 tiles of it) into one row of this new “store data” list! Hmm. Luckily my last tutorial
named “Save My Game!" 💾 Data Serialization & Escaping in Scratch” has the answer! If you
haven’t followed that tutorial yet then please do that now, there’s a link to the tutorial here.
[Backpack the Data Serialization Sprite] And here it is. These are the two custom
blocks that we are after, and I’m going to open my backpack and drop the entire sprite in there.
These two scripts let us take lots of data and serialise them into a single long value,
and then de-serialise them again back into their original state. Exactly what we need.
Ok, open up our RPG episode 9 projects. And, before we proceed any further – Don’t
forget to save them as a new copy, it’s very important this time as any mistakes
could mess up your beautiful levels. Guys, this is episode 10! Let’s Get Scratching!!!
We need to copy the two scripts, from the backpacked “Save Game” sprite into
the existing “Level Store” sprite. That’s where we’ll code up the saving and loading of levels.
Cool – All our coding today is going to take part in the “Level Store” sprite. And,
those new helpful scripts can be moved well out of the way.
[define Save Scene] Here we go! We’ll begin by serialising (that
is saving) the existing scene into a save code. A new custom block, “Save Scene” will do this,
adding a text input to give the scene a name. Then, run without screen refresh.
Start with a blank “save code”, and then we’ll write out our first value: “RPG”.
It’s common practice to begin any serialised data with a header such that when we load this
data back, we can then check the first item and confirm it matches the header we were
expecting “RPG”. A safeguard if you will. We can see that in action by clicking “Save
Scene”. Save code reports “RPG|”. Great, we’ve begun!
[Modular Save Code – Save Grid] Next we want to add the contents of
the Grid List itself. We will call this a data module as there may be more type
of data to store in future. Create a custom block for this named: “Write Module: GRID”.
Drop that in right after writing the RPG header here.
Now, each module will, like the save code itself, have a header value.
This one named “GRID.1” – This means it’s the first version of our GRID save module.
Saving the scene shows “RPG|GRID.1|” – So good so far.
Next we write GMAX, that’s the width of the 2d GRID (100 tiles across). And,
for completeness, we’ll also write the height. Also, GMAX since our tile map is square – But
let’s not assume that won’t change in future. And the save code is getting longer! Love it.
[Run Length Encoding the 2d GRID List] Right, guys. This is where things get a
little more fun. The GRID list we want to serialize contains a lot of data,
and it’d be preferable if we could shrink it down a lot as we save it away.
The technique we’ll use is called run length encoding. It’s useful because our GRID list
contains long runs of the same value. Now rather than storing “79|79|79|79|79|79|79|79|79|79|”,
we instead store just one 79 and before that, the count of how many there were:
“10|79|”. That’s a run of ten “79”s. See how much smaller that is? And that’s run length encoding.
Let’s do it! We need a variable “gidx” (for this sprite), to keep
track of where we are in the GRID list. Set that initially to 1, the first item.
And then we need a variable “tile” (for this sprite),
that keeps track of the actual tile in the list at this position; from “Item gidx of GRID”.
Finally, a variable “count” (for this sprite), so we can keep track of how
many tiles of the same type we’ve found. And right now, that’s just 1. This first tile.
We can use a repeat to loop through each item in the GRID list.
Repeating for the “length of GRID” list, except subtract 1 since we’ve already got the 1st item.
Changing gidx by 1 to move on to item 2. And then, with an IF ELSE block, compare this
next GRID item, with the previously “tile”. If it’s the same tile again, we change count
by 1. Now that’s 2 tiles of the same type, great. We’ll repeat around again and check the next tile,
until finally… boom we hit a different tile number and then we need to take action!
First, we write out the previous count (how many tiles we found).
Followed by the previous “tile” number. And then, Set tile to the new tile type,
item gidx of GRID. And Reset count back
to 1. The first of a new run of tiles. Ok, so that is pretty much the shape of it. We
loop around counting up runs of tiles and record the results when we find the next tile type.
But, be careful, because when the repeat loop ends, we still have a tile and count to record.
So pop in a final write count, and write tile down below the loop as well. Perfect!
Lastly, we’ll terminate this grid module with a single write “END”.
Well then, I think this is a great time to give this another test, don’t you.
“Save Scene”, Oh yeah – wow, so as expected this is now much longer, just how long though?
Length of save code! Oh my, almost 11,000 letters! That’s still pretty large,
but then my scene is pretty complex. But, compare this to the uncompressed GRID list at
34,000 letters, then we’ve managed to shrink it to a 3rd of it’s original size, so good job!
[Storing the Save Code in the Scene Dictionary] Let’s scroll back to the “Save Scene” custom
block. With the save code complete, we now want to store it away in our Scene Dictionary… Which
we haven’t yet created, so make a list named “store name”, for this sprite.
First thing to ask is whether this scene name is already in the list.
Use the “item # of list” block. This gives us the item number of the input variable “name”,
if it is found in the “store name” list. We’d do well do keep track of this,
so make a new variable “store #” for this sprite, and pop the item block in there.
Now, if we didn’t find the name, then item # will be 0, in that case, this is the first time
we’ve tried to save this scene. No problem. We just add it now.
And then ensure “store #” is also updated to the same new row number. We could use the
item # to find it, but why waste time when we know it’s the last item in the list.
Test that with a Save Scene, with name “cake”. Nice! – A new item for cake has appeared,
and store # is 1. Importantly,
no matter how many times we click “save”, we only get the one item added to the list.
Saving under another name is a different story, for that adds a new row and updates store # to 2.
One last test now, try saving as “cake” once more.
Perfect, “store #” points back to 1, the first scene. Exactly right.
So that’s the name stored, what about the scene data?
We need another list, “store data”, for this sprite.
To keep them in sync, whenever we add an item to the name list, we also add an item to the data
list. A blank value at first. Before we can test this out,
we must first manually delete all the values from the “store name” list.
Very nice… A matching value-data pair! But the data is very
empty – Let’s stuff in the save code. Replace item in the “store data” list,
at position “store #”, and push in that serialized “save code” value.
Exciting stuff! – Running “Save Scene” again and now the save code is indeed showing up
in our data store. That’s so cool! You can try saving under different names,
and this should all work tickety-boo! [What name to give our Scene?]
Of course, cake and fish are not the most suitable names for our Scenes. I’ll
delete all of those for a fresh start. To keep track of the active scene we are
viewing, make a new variable, “SCENE NAME” for all sprites.
This first scene can be named “over”, short for “overworld”. But add on the end “:0:0”. This
will allow us to have multiple overworld scenes in an x y grid. The scene to the
right of this one would be named “over:1:0”. Click to set the variable, and save the scene
officially now, under the correct scene name. Brilliant – We are making serious progress!
[Loading a Saved Scene] And now, for the second part
of this episode, where our attention turns to the loading of scenes!
If we look back at the first script in this sprite, we have a when I receive
Store – Load Scene. We set this up yonks ago, and it’s called right at the start as
our game begins to run. Originally we were generating a new map each time.
Well now we are going to Load a new Scene, with a given input,
name, instead. Run without screen refresh. As the game begins, make use of that block,
and drop in the current SCENE NAME to be loaded. I’ll define the new block over here next
to the save scene so we can compare. Indeed as we want to load the save code
from the level store, we use the same set store # and IF check that we used in the save scene. Only,
when the scene doesn’t exist, we’ll instead generate a fresh new map, and just stop there.
But, if the scene does exist, then yay, set the save code to the item in store
data that matches the name we just found. It’s at item “store #” too.
Before we can start reading values out of this save code, we must set save idx to 1.
This is the start of the save code, and is used by the read value custom block.
And so, we read our first value from the recovered save code.
Do you know what value we are expecting? Well it should be the save code header… RPG right?
We can test that easy enough with a “Load Scene (SCENE NAME)”.
Click that, and then check out the value of the value variable… “RPG” – Perfect.
But… if it wasn’t RPG, then we should not continue to load the save code. We’ll also
pop in an Ask block to pop up an “Error loading Level” message. And then stop this script.
The next value we’ll read out of the save code is the module name “GRID.1”.
But don’t assume this will always be the case, modules may be in any order in the future,
so we’ll use a forever loop to read through the modules.
And check IF the value, the module name, contains the word “GRID.”.
So any version of the GRID will be accepted here, 1, 2, 3, etc...
Let’s make a new custom block to load this module, “Read Module: GRID”.
And we’ll make use of it right away. The else is run if the module was not a
GRID… In that case check to see if this is actually a module at all,
if blank then we are at the end of the save code, and we can simple stop this script, all is done!
The last case is that we’ve found an unexpected module – So another error
I’m afraid: “Unexpected Save Code Module”, and join on the module name we just found
so we know why it went wrong. And Stop All! [Decoding the Run Length Encoded GRID module]
Now straight back to the Read Module script. Again I move it to line up with
the Write module grid script. Firstly, we won’t have to worry
about reading in the module name as that’s already been read for us to get this far.
So the next thing to read will be the grid width “GMAX”.
Set GMAX to value. But I’m going to round this value to ensure it’s stored as a number. This
can improve performance just a little bit if the variable is used a lot.
The next value is the grid height… and we can just read that in, and throw it
away for now as our level grid is square. Right! Next up is the run length encoded
grid data itself. Before we begin reading, we’d do well to delete all of grid to start afresh.
Now we’ll loop around reading the run length count and tile type values.
However, by reading the first value outside the loop, we can
repeat until value = “END”. That means we’ve reached the end of this module.
Otherwise, we loop. Setting count to value, so we can read another value, this being the tile type.
So, we use these variables to add a run of ‘count’ tiles, Adding the tile type stored
in the value variable to the GRID list. Finally, before we loop back around,
we need to read another value, again this will be a count, or the END value.
Brilliant. The module is loaded! Let’s nip back to the Load Scene
custom block then. Once the GRID module is loaded, we’ll loop back around for another
module… and should find a blank value as there are no more modules to read in.
So, back to the when I receive Store – Load Scene.
And well, that’s it. That’s all it does, so job done.
[Testing the Scene Saving & Loading] So this is where things get a little dangerous! We
need to test our loading code, but as soon as we try to load a scene, if it fails then it’s quite
possible we’ll mess up the current level. You may like to save the project as a
new copy right now to ensure you can go back to the previous one,
or export your GRID list to a file, which again can be re-imported if things go wrong.
Ok, enough warnings! Here we go. I’m click the New Map block…
This will clear our active scene, so say goodbye! If we want to see this blank scene,
then separate off the load scene block temporarily, and then run the project.
Yep… yep… It’s gone. My wonderful level has been reset.
But… if all has gone well, then it’s still stored safely away in our scene data list.
So, cross your fingers, and smash that Load Scene block…
And yes! Phew! We did it… The level has been restored. We can have a walk around and make sure
it all looks exactly as it did before. [Outro]
This is great - the Load Scene block can be hooked back up to the event receiver. So, we can set the
SCENE NAME to any name we want, and when the game runs, we’ll begin with a fresh new map.
The new scene is not added to the data store list until we manually hit that Save Scene block. Like
so… But this is great, as we can then save and load these scenes at will. Of course,
it won’t be perfect until we can do this directly in game and from the level editor. So, in the next
episode we will be integrating this with the level editor, AND will introduce scene transitions as we
move off the edge of one scene and enter another – I’m really looking forward to that! Are you?
Well guys, I hope you enjoyed this tutorial – If you did, you can show your appreciation by
smashing that like button, and do comment under the video if you have any ideas,
or just to say hi! Also, if you haven’t already, now is a great time to subscribe
to the channel so you get notified as soon as the next exciting video drops.
That’s it for toady. Thanks for watching! Have a great week ahead – And Scratch on Guys!