[Intro] Hello Fellow Scratchers,
and welcome to this fun tutorial on Data Serialization… Did someone say save codes?
Ah Yes so useful – Allowing players to save their progress & enjoy our Scratch games for longer!
Also, data serialization unlocks the key to adding multiple scenes to our tile based RPG too.
Now, to understand what this is all about, take a look at this
oddly familiar platforming game. Hey Guy! To create a save code, we’d need it to take
a record of all the data values at one point in time (sprite positions, count of lives,
score, whatever is subject to change). Here comes “Serialization”, where the values are
translated and combined into a single long value (also sometimes called a data stream). And in
Scratch, we might refer to it as a “save code”. The most important thing about serialisation,
is that the process is also reversable, and will allow us to reconstruct our game
at just the same point that we saved it. So, in this tutorial we’re going to learn
how to build two very useful custom blocks; “Write Value” & “Read Value”.
Very cool! [Comma Separated Values & Delimiters – CSV & DSV]
And you may already have noticed that the save code we’ve created is a CSV (a Comma
Separated Value) except, we don’t need to use commas. I’ve used pipes, and you’ll
often also see spaces used too when appropriate. And I say, when appropriate, because look at this
example where we join two values: “Hello”, and then “Fellow Scratchers”), and if we try
separating them with a space delimiter, then… oh man, we can’t tell that this was two values any
more… it looks like 3! But, easily solved right, we just pick a better delimiter, Like a pipe. The
rule would be never to choose a delimiter that will appear within any of our values, but…
That’s not as easy as it sounds: (“Fellow :| Scratchers”) – For no matter what letter or
symbol we choose as a delimiter, there’s always a chance that it might get used in one of our
game variables AND completely break our game. But don’t throw your keyboards out the window
quite yet, for “Escape characters” come to our rescue! I’ll explain more on these shortly,
so keep watching until the end of the video for the complete picture, but gosh I think
we’d do well to begin coding this up! [Basic Write Value Custom Block]
Guys – Let’s Get Scratching! This is going to be great to backpack,
so we’ll create a brand new project for it, and the sprite will be named “Save Code”.
The first thing we need is a variable to hold the save code. For this sprite only.
And set it to the empty value. Good.
Now we make the new custom block, named “Write Value”. It has an input, named “text”.
And, we definitely “run without screen refresh”. This has to be fast!
Then, to join the value of the text input onto the end of the existing save code;
we set save code to (itself), save code, Joined with, the new text input value.
But, don’t forget to add the delimiter too, our pipe character.
Duplicate that line, and JOIN with the pipe. Let’s see this in action. We’ll begin by setting
save code to “”, and then write the value “Hello”. If we give that a click, you can see we’ve added
Hello and then a pipe delimiter to the end. Great, so following that by “Fellow Scratchers”,
and the number “123”, and we see all three values serialised appropriately. Very good.
However, although this is an ok way of serialising values, we will run into trouble
trying to write values that containing that pipe themselves. We’ll come back to that later.
[Basic Read Value Custom Block] For now, let’s move on. We need
that “Read Value” block to deserialize our values back out of the save code again!
Make a new custom block, named “Read Value”. Again run without screen refresh.
Each time we run this it want’s to return a value, so make a new variable for that,
named “value”. For this sprite only. This must start empty.
We’ll want to loop through the save code, building up this “value” a letter at a time.
We don’t know how many times to repeat yet, so “loop forever”,
because we can always break out of it later. Let’s get the first letter of “save
code”. Hmm, except we need to be able to loop through a letter at a time, so we need a variable
to say where we are up to. Name it “save index” (or save idx for short), for this sprite only.
We begin at the first letter, set save idx to 1, and then, we can get letter “letter
idx” of “save code” – So that should begin as “H” – perfect! “H-ello Fellow Scratchers”!
We’ll store this H in a new variable, name it “c” for character, for this sprite only.
And set it. Ok, now we have
the letter at position 1, we change save idx by 1 all ready for the next loop around.
But before we go ahead and add this to our value variable, What “IF”, the letter we just found,
in variable “c”, is equal to a “|” (pipe)? Well, then we would have found the end of the
next value, and we can stop this script, breaking out of this forever loop.
And, if it’s not a Pipe, then swell – set value, to the result of joining,
itself, value, to our letter, in variable c. I’m loving that. Because after adding that letter,
this repeat goes around again, and now we are looking at letter 2, and 3, and 4, building up
this value until we hit a pipe. Shall we give that a test?
Start by setting save idx to 1 with a click. And then, drop in a “Read Value” block,
and click that. And there we go! Value
has become the word “Hello”, exactly 5 letters long, and perfectly formed.
If we click the “Read Value” block again… We get “Fellow Scratchers”…
And again, “123” I love that, don’t you? [Index out of bounds error and Optimisations]
Ah, but now we’ve reached the end of the save code,
who wants me to click this block again to see what happens next?
And boom!!! Scratch has gone into melt down, this script is running
forever!!! STOP THE PROJECT – if you can lol. Ok, this is not the best result. When there are
no more values to read, it would be preferable to just return an empty value and not run forever.
How would we do that? Rather than stopping just when c = “|”, we could also stop when
“save idx” > length of “save code”?. That would make sense right? We’ve gone passed the end?
Well, we could do that, and it works… but that’s a bit of an expensive, long way to do it.
It just so happens that any letter after the end of a value equals the empty value. So we can just
compare “c” to the empty value instead. See, still works a treat!
But! Did you think that was the best way we could do this? Ha! There’s one more way that is even
shorter! And that’s using the contains block. You see, “apple” contains an “a”,
yep no surprise there. And “|” contains a
“|”… yep apparently so, good good… So tell me, does a “|” contain… the empty value?
– Well that’s a tricky one… Maybe?
Yes! It does… interesting, so I guess every value also contains the empty value.
So… instead of checking whether c = pipe or the empty value, we just check if | contains c. That
means c is a bar or… the empty value, Perfect. [Write with Escaping]
So, guys… Everything is working great. We can happily serialize and deserialize values,
so are we done? Well, we could be, if we are happy to take responsibly that no value written
to this save code will itself contain a pipe… If unfortunately we can’t guarantee that… then,
well see what happens shall we. We wrote three values in to the save code,
so there should be three coming out. Good, Uh oh! “Fellow :”? “ Scratchers”,
and “123”… That was 4 values… And we all knew it was going to happen.
So, how do we fix this? The answer is, using escaping.
How this works is, whenever we ask to write a value containing a pipe. We escape the pipe by
preceding it with a backslash. This tells our serializer to treat the next letter as
part of the value, and not a delimiter. We’re going to require some space to work
on the write value script as we’ll now need to process it a letter at a time,
looking out for pipes… Sound familiar? We’ll start at letter 1. Reusing “save
idx” for the job. And this time,
we know how many letters to loop through, “repeat” for the “length”, of the text input.
Next up, we get a letter. Set c to letter save idx of… but not of the save code, this
time we are looking through the text input value. Ok, so now is the time to watch out for that pipe
character… If c = | then this needs escaping. That’s as simple as adding a backslash
to the save code, just set it to itself, joined with a backslash.
Followed by the actual character, but put that outside the “if”. This way we add every
letter to save code, but the pipes are always preceded by a backslash, does that make sense.
Finally, we mustn’t forget to add on that last pipe to finish off.
Wonderful, I think that should do it… Smash that test script up here…
What… The…… HHH FFF 111? That is not “Hello Fellow” Anything!
I wonder what I did wrong – If you fancy a challenge, can you figure out what it
was? Just pause the video and take a look at the script. If you got it then drop a Fixed
it in the comments so I can applaud you 😊 So why would we be getting the same letters over
and over again… Perhaps because we never update the “save idx” variable… it stays on letter 1!
Simply drop in a change “save idx” by 1 at the end of this repeat loop.
Right! This time we are in for a treat – Our 3 values have been serialized,
bug squished… And this time, the emoticon pipe is preceded by a backslash! Nice 😊
[Read with Escaping] Only problem is, now we need to be
able to handle reading back these backslashes. If I scroll down to the read value script, you’ll see
that the second value we read still breaks at the pipe, but worse, that extra backslash is showing
as part of the value! That’s not right, we need to catch those backslashes before they are added!
Put an IF before the last set value, and check if c is a backslash.
If it is, then we throw it away, because it’s the next character we are interested in now.
The top two lines in this forever loop do that job, so duplicate them and pop them in the IF.
So, if we find a backslash, get the next letter instead and join that to our value in it’s place.
Solid. Let’s give it a test. Set save idx to 1, and then read value. Good.
Read value again! Oh boy, now this is more like it, we’ve successfully read the correct value,
including the pipe, and the escaping has gone 😊 – I love it when a plan comes together!
[One last issue!] We are almost done here – But
are you thinking what I’m thinking? We solved being able to include pipes by escaping them
with a backslash symbol, but doesn’t that mean that we have to be careful not to add
backslashes to our written values? Yes indeed, it appears we just swapped one problem for another!
Yep… The backslash is missing! But luckily this has a simple solution that fixes everything.
Under the write values script, we currently only escape pipes. But what it we escape
backslashes too? Simple as adding an or here, but I’m going to do them both using
by favourite contains block again. If “|\” contains c then c is either a | or a \.
Interesting! So what does that now look like after it’s serialized?
Well the backslash has, as we expected, also been escaped by adding a backslash before it.
Remember any letter after a backslash will make it into the read value, so I’m expecting the second
one won’t be skipped this time around. Set save index to 1, and read… Hello,
“Fellow :\ Scratchers”! – So awesome, a perfectly accurate result.
[Summary] And that is it,
we have created a very robust data serializer. If I were you I’d make a copy of these scripts
just as they are, because they will come in very handy for multiple projects in the future,
including the next episode of my popular RPG series. In that episode I will demonstrate
how to create backwards compatible save codes & efficiently serialise a complex grid list map.
But just as we end, let’s just review how we can use these two blocks.
To create a save code, we set “save code” to the empty value,
and then write each value we want to store in turn.
And then, to load these values back out of the save code, we set save idx to 1,
and then use three read values, one for each of the write value.
Of course it doesn’t make much sense unless you actually have some variables
to save… So let me create some as a demonstration; player x, y & lives.
Each of these I write to the save code. Then, to restore each of these,
we need to pop in a set block after each read, to assign the read value to the correct variable.
Just watch this, I’ll make these into sliders… and then set them to some random values…
Now click here to create our save code.
Then, I can switch these sliders around… but if I click to load from the save code… all
three sliders return to their saved locations 😊. Often to allow a player to download a save code,
we’ll add the save code to a visible list. We do this because it’s the only
way to make text copyable by our users. A show list can be used to make it visible.
And then, to load a save code, just pop in an ask block.
Finally set “save code” to the users answer… And hey presto, we can restore our game!
[Outro] Well, I hope you enjoyed this video,
if you did then please smash the like button and subscribe to the channel so as not to miss
the next exciting episode when it drops. I look forward to continuing this series with a detailed
look at how to construct backwards compatible save codes in the next episode of the RPG series.
However, this technique is super useful for all sorts of projects, and I’d love to hear from you
in the comments what you have planned for it! And that’s the end of the video,
thanks for watching – Have a great week ahead, and Scratch on Guys!