Welcome back.
Now that we have a function to save the world, we need a function to load the world.
So let's go to aura game mode base.
And just under save World state we'll make a new function called Load World State.
And this can also take in a you world pointer called world.
Let's go ahead and generate the definition for this.
And let's think about how we can go about loading the world state.
Well, first of all, we need to know what that world name is.
And just like we did up here and save world State, we're going to get the world name through git map
name and remove from the start of it the streaming levels prefix.
So let's get those two lines right here.
And I'd also like the game instance.
So I'm going to get these two lines where I'm getting or a game instance as well.
We'll go ahead and get those.
And next I want to check to see if the save game exists.
It has to exist if we hope to load it.
So we're going to check if you gameplay statics.
Does save game exist and we're going to check for the slot name using Aura Jai.
Load slot name and or a GI load slot index.
If this save game object exists, we can then proceed to iterate over the actors in the world, at least
those that implement our save interface, and we can load them.
We can do anything we wish, such as restore those serialized variables that we serialized and perhaps
call a load function on them.
They have an interface, after all.
So what we're going to do is iterate, just like we did up here.
We're going to use f actor iterator, iterating over the actors in the world.
I'm going to copy the line where we dereference the iterator as well.
We're just going to need a closing curly brace there.
And once we have that actor, we can filter out actors that don't implement the interface.
We can say if not actor implements the you save interface.
Don't forget your parentheses there.
And we can use continue.
And that way we only continue for actors that do implement the interface.
Okay.
So what next.
Well I want to get all the actors in the array for the f saved map that has this map name in our save
game object.
So where's our save game object.
We need to get that don't we.
Right here inside of does save game exist our if statement.
Let's load it.
Let's use you load screen save game.
We'll call it save game and we'll cast to you load screen save game.
What are we going to cast?
Well let's load it with you.
Gameplay statics.
Load game from slot using our aura GI.
Load slot name and our aura GI load slot index.
Don't forget your semicolon.
Now we have our load screen save game here.
Right.
Let's check if it's null.
Let's say if save game equals null pointer.
And let's go ahead and log an error.
Let's use u log log ora.
Error text and we'll just go ahead and say failed to load slot and we'll return.
So something's gone wrong.
If we haven't been able to load.
Now that we have our save game down here, let's iterate over its map that has the world name that we
want.
So in other words, let's say for f saved actor, saved actor and save game and we have a function get
saved map with map name passing in world name.
Now this is the F saved map, which contains the array of saved actors which we can access through the
dot operator like so.
So we're getting our save game object.
We're getting the map with this name and looping over it saved actors.
And for each saved actor we're getting that data.
And this is already inside of a for loop looping over all the actors, at least those that implement
the interface.
So let's check if the names coincide.
Let's say if saved actor dot actor name equals actor get f name.
That's how we got it in the first place, after all.
And if these actors have the same name, we want to load it up.
Now remember we saved the transform.
But my checkpoints are not mobile.
They're not something that we should update the transform for.
That's okay.
We can give our save interface a function that can return a boolean.
If we want to know whether or not we should update the transform for whatever reason, right.
So we can take our saved interface and create a bool returning function should load transform and we
can give it a new function.
Let's give it blueprint callable.
Let's give it blueprint native event.
To make it easy to call.
And let's give our checkpoint the function.
Let's go ahead and make a comment here that says save interface.
Let's close it off with end save interface.
And let's override bool should load transform implementation override.
And for this I'm just going to implement it here.
Return false.
And this should have the virtual specifier of course.
So for checkpoints it's easy.
Don't do it right.
Don't update transform on load.
And here in the Or game mode we can check that.
We can say if and we'll use I save interface execute should load transform passing in the actor.
It has to be the actor, not the saved actor, right?
The saved actor is just a puny little struct.
This function needs the real actor.
And if we should, which our checkpoints are returning false.
Here we're going to take the actor and set actor transform to the transform and our saved actor struct.
So just there, in case you happen to have actors you do want to update the transform for, just remember
you have to implement should load transform.
If you implemented it this way that we did here.
Now we also are going to want to deserialize all those variables that we serialized using the fancy
archives above.
Now, those fancy archives kind of work the same way in reverse.
So we make an F memory reader.
We can call it memory reader.
We initialize it with the saved actor dot bytes, and we create an F object and name string proxy archive.
We can call it archive and we initialize it with the memory reader.
And true just like last time.
And it's for a saved game.
So we take archive dot R is save game and make that true.
And then to deserialize our actor we take the actor and we call serialize on it.
Yes, it's serialized and not deserialize, but in this case we are deserializing it.
And all of those binary bytes are converted back into variables.
We'll say converts binary bytes back bytes.
I don't know why I want to put an L there.
Bytes back into variables.
There we go.
Okay.
So we've deserialized all those bytes.
And the last thing we can do is anything else we want to do when loading something.
Right.
For instance our checkpoint has a reached variable.
And if we want that checkpoint to be glowing and disabled as far as its overlap sphere is concerned,
as soon as we load it, then we can have a function on our save interface for loading up the actor.
So we can say right here and save interface, we can make a new function.
By the way, I forgot my semicolon there, but we can make a new function called void load actor, for
example.
And let's just make this a blueprint callable blueprint native event and go ahead and implement this
in the checkpoint.
So right here next to our other save interface function it'll be virtual void load actor implementation.
And here in load actor implementation let's just say we want to check our be reached variable.
We can say if be reached then we can just start glowing and deactivate basically everything we did in
Handle Glow effects sphere collision enabled.
Set that to no collision.
Replace the material with the dynamic material instance and then call checkpoint reached, which is
a blueprint implementable event that will run a timeline and glow.
So load actor implementation if reached is true, we can simply call handle glow effects.
So now that there's this load actor interface function, whenever we load an actor, we can call that
we can take I save, interface and execute load actor and pass that actor in the real actor, not the
saved actor struct proxy.
All right.
So now we have functions to save and to load world state.
And the crazy thing is these could both be const.
So we might as well be const correct here.
And make them both const while we're here.
So we can add those to both the function declaration and the definition.
There's save world state.
Here's load world state.
And now that we have these we need to decide when to call them.
Now I'd like to save the world state every time we reach a checkpoint.
So in on sphere overlap when we're telling our player interface execute save progress.
So basically for our aura character we're telling it go ahead and save your progress.
Handle glow effects.
While I'm at it, I'd like to first of all, first things first set be reached to true.
Because if we save world state before setting this boolean, it will not be serialized.
So we have to do that first and then after setting be reached to true, I'd like to get the game mode
and tell it to save the world state.
Let's do that.
We'll make sure after we set be reach to true.
So we're going to say a aura game mode base header included their aura GM equals cast to A or a game
mode base, and we'll get it with you.
Gameplay.
Statics.
Get game mode.
Passing in a world context object.
You know what it is.
And.
If this is valid.
We'll take Aurum and call save world state.
And it takes a world so we can pass in get world.
So on sphere overlap, we'll go ahead and save the world state.
Now when do we load it?
Well, let's go ahead and load the world state when we're loading up all of our player variables in
the character class, let's go to private character or a character dot cpp.
Oh, I'm noticing I have a dead include here for the game instance.
I'm going to get that out and I'm going to find possessed by where we're calling load progress.
Right here is where I'd like to actually call load World State as well.
I'm going to copy this line where we're getting our game mode.
I'm going to wrap it in an if statement.
And take our A game mode and call load world state passing in get world.
And with that we're saving.
We're loading.
All we have to do is test things out.
So let's compile and launch.
Okay.
So I'm going to first of all go into my maps and to load menu I'm going to press play.
And I'm going to delete this slot and make a new one.
Select it, press play.
Let's go ahead and reach a checkpoint.
There we go.
We can exit, come back, select that slot and press play.
And look at that.
It's starting to glow now.
The cool thing is I can go over to this checkpoint over here and reach it and exit and come back.
And not only are we at this checkpoint, but it's already glowing and we can come back and check the
other checkpoint we've previously reached, and it's glowing as well.
So we're saving the world state.
We're saving that breached boolean.
And you know, we went through an awful lot of effort just to serialize and save one single boolean.
But the implications are much vaster than that.
We can now save an arbitrary number of variables that have the save game specifier.
And we can save not only an arbitrary number of actors per level that have our save interface implemented,
but we can also save different actors in different levels, because that's my whole goal here, is to
be able to transition from one level to another and back again, and load up all of the saved data for
each world.
I'd like to be able to remember which actors we've saved, which checkpoints we've reached, in which
level.
That's what I'm interested in.
If I reach this beacon here, then go off to another level and then come back, I want this beacon to
be reached.
Still.
I want it to still be lit up because I've reached it already.
So that's what we'll do in the videos to come.
We'll develop a system to travel from level to level and remember what we've saved.
Great job and I'll see you soon.