 Saving data to your player's file system and loading that data is fundamental in pretty much every single game. Whether you go back to the days of the high score leaderboards, in arcades, to the modern era of tons of complex data of maybe you have an RPG with multiple party members and their levels and equipped items and number of enemies defeated, etc. All of that data needs to be tracked somewhere. And the common aspect, regardless of the level of complexity, is that you're saving data to the file system and loading that data from the file system. Whether it's in a plain text file, a JSON file, a database like SQLite, whatever it is, it's a really common need. In this video, we'll go through how to write that data to the disk and then how to read it and then update your UI or your state accordingly. I've got a game set up called High Score Tracker. When you press the start button, a five second timer starts and you keep track of how many times the press me button is pressed. And you can see down there, I pressed it 11 times. If I click start game again, it resets it. What we're going to do is we're going to keep track of the high score. And then if the number of times pressed in a different play session is higher than the previous high score, we'll display some special text and we'll write that to disk. And then we'll also make it that when we load our game, we load the high score from disk and I'll talk to you more about user space and writing data to user space. So that's the core. Let's get into it. The first step here is that we want to keep track of high score. So we'll go ahead and in our main script, let me bump this out a bit and hide this area. I've already got variables set up. Times pressed is what's getting incremented every time the gameplay button is pressed. And we also reset it on start. There's nothing else too complex here. We're setting some labels and it's all pretty utilitarian. But we've got a variable high score that's not being used at all and it defaults it to zero. So all we want to do is on gameplay timer timeout. That's when gameplay ends. We're just going to check if high score is less than times pressed. That's the times pressed in that session. We set the high score to times pressed. Now that won't be very visible yet. So let's go ahead and add a label to our scene that keeps track of the high score. So I'm just going to go add a label. We'll name it high score. Drag it up here just to organize it a little bit. I'm going to say high score zero. And we'll make it so that our code sets that dynamically on loading. But for now we've got high score there. Then what we'll do is we'll go here back into our code and we'll say set high score text. Similar to set pressed text. I'm just making this non fancy, you know, really just keeping it clear. And nothing, you know, no setters or getters or anything like that. You could use those here. But for us let's just keep it simple. We'll call a high score text. I'm going to change this label to be high score text to match pressed text. And let's even change that to be label. And then we'll rename them to be label here. Okay, that's feeling better. We'll set it to be high score. Our value there. That's our variable high score. Run the game. It's set to zero because we don't have a high score. I'm going to press this two times and we'll let the timer run out. Great. High score was reached. Perfect. We'll press start game again. We're still two. I'm going to press it five times. Okay, great. It changed the high score to five. We know that that's working. That's excellent. That is what we want. So now what we need to do is save that to the file system when that happens. But well actually first let's just do one quick thing. We'll say funk ready. And we'll just call our function that we just created set high score text. And that will set it to be our custom value using our function and it'll use the default at high score. And I think that's just a good thing to do. Like really quick aside, I like using the 2D scene view with dragging nodes in and laying it out. I view it as a sort of mock up. It's not what the player will see. It's not necessarily populated with the data that might be loaded from somewhere else. Let's me see, okay, here's what this would look like with this placeholder text. And then in the code, in the script, you know, we'll actually set it here. That helps me kind of lay things out and then I go and implement the dynamic functionality. Anyway, let's go ahead and implement our saving of the high score. And we'll just say if high score is, if the previous high score, that's what that represents there, is less than the time to press reset the new high score. So we'll go ahead and we'll say set new high score. We'll make a function that does what this used to do because this makes it a little more clear, right? This is going to get a little more complicated and we're going to want to have this down here so we can easily make use of it. We've got a warning down there. We'll ignore that. So what we want to do is in the previous screencast, I talked about how you read data from the operating system of the computer. Now we're going to talk about writing data to the operating system of the computer. It uses a very similar API called file access. So you can do file access, dot open. And you may have seen in other parts of the dough res colon slash slash. That's a resource from our game file. But this isn't a resource from our game file. This is a file that our user, aka our player, is updating and creating. And maybe there are different users on the computer like operating system users. This would save it in a different spot for each of them so they could have different high scores and still play the game. Also this needs to be an assignment. Sorry about that. But so this saves it into user space, which is different than resource space in your game. And we're just going to call it high score dot txt. And we need to set the proper permissions, which is going to be file access dot write. So now we have a file access saved in a variable called file. All we're going to do then is call file dot store string. And we're going to pass in high score. We get an argument error. It says it should be string, but it is in. So is there some way we can change that? I know we could do our percent business where we convert it to a string. Let's see if that does what I think it does. Let's go ahead and test it. Thanks for bearing with me, you know, sometimes I don't know everything. Oh, no, many times I don't know anything. So, you know, we're learning, we're trying things together and we're experimenting. So I pressed it, high score is set to eight. In theory, this should have written to high score dot txt. You might be wondering, where is high score dot txt? Well, that varies dependent on your operating system. The Godot docs have a table that explains this. And on Windows, it's in this direction, directory. In macOS, it's in this directory. On Linux, it's in dot local share. I'm on macOS right now, and I've actually got this open already. So we can go in and we can see here are all of my different Godot games I've run. And it saves different data there. Here's high score, the project I'm working with right now. And look at that, high score text. And it set it to eight. That's pretty cool. And we could even modify that and change it ourselves. But for now, we'll just keep it there and let it be. So let's go ahead and play our game again. And we'll press it more than eight times and set a new high score. Okay, the high score is 14. Let's go back to our finder. It's 14 now. So our high score tracking is working. We're taking data from our game, a variable. We're saving it to disk to then persist. And now we want to go and take it and load that into our high score. So instead of defaulting high score to zero, what we're going to do is we're going to say load high score text. We're going to just call load high score and we'll make a function called funk load high score. And we'll just have it returned void and not have it do anything special. That's optional that type signature. But you know, as always, I keep the type signatures on because I like to keep that there. I'm going to put it near, yes, set new high score load high score. That's works for me. We're going to set the high score text because I think that something we still will want to do. And instead of writing the file, we're going to read the file. Much like we did in our other video about reading text from a text file, we're going to just go ahead and do a similar thing. We'll say var file is equal to that. And then we'll set our high score variable and our script to be file dot get as text and skip carrot returns. I think we want that to be true and we'll call to int on it. So that will take our text in our file. It will skip the carrot returns and the returns and it will convert it to an integer. So now if we go ahead and run our game because we're calling. Let me expand this. Sorry about that. Let me expand this and we'll because we're calling load high score on ready when this scene runs. That goes ahead and calls our function and we read it from the disk and load that file. Let's see what we get high score set to 14. There we go. Now I'm going to open this in my text editor and we'll tamper with the saved data and we'll make it. Some really high number and now it's that really high number right. So there's an issue or is that a bug or a feature right like maybe you want to make your game in such a way that people can easily edit the saved data to. Change things right because it's a game and it's for fun and maybe having control over that helps them. But for our case. It's fine for this demo but what you can do is you can encrypt the data if you want and have a secret key that is used to make it so that. This gate is obfuscated but that's beyond the scope of this maybe that make for a good video in the future. But for now we've got high score working. So if we start our game we loaded the high score three from disk if I press it more than three times. We have a new high score of 13. And if I close this buffer over here this file and then reopen it preview it now it's set to 13 it's a little small sorry about that. But there we go we've got high score tracking and loading working and let's at the end let's just do something funny and say. I'm a string what happens if we change this to a string and we try to convert it to an in let's just see what happens. Oh Godot smart right Godot knows to safely make that zero and fail there so nice job good to hope that's in GD script that's really fantastic. Let's just test it again make sure it works. Okay new high score of 20 view that from disk it is set to 20 and there we go. You can take save data loading and changing and you know as far and complicated as you want it's the skies the limit there. But the gist of it is is that when an action occurs maybe a save button is pressed maybe you have auto save. You'll go ahead and you'll open a file and you'll store some data in there maybe it's a JSON file maybe it's a text file right like I said before. And then when a scene loads or your game loads or player presses the load button you'll go ahead and read that file get the data in some format. Instead of get us text you might be able to do JSON there's a there's a way to load JSON into dictionary which is you know beyond the scope of this. There's ways to have your data in different formats but you take that data put it in a variable and then you'll update your state and your UI accordingly. And that you know this is a very simple example but you can see hopefully then how you could take this and just slot it in wherever you need it and. It's not not overly complicated and you got to be careful for certain certain things like if users can input data or data can get a funny place in a funny way. But generally that is how you read and write data that your players entered. Don't forget about user space different than res space right if we change this to res let's just do it just to see what happens. Oh interesting attempt to call get us texts in base null instance on a null instance we found a bug actually in the code let's fix it. I thought the video was over but I'm back for an encore so let's go ahead and actually fix this bug and we'll say if file dot file exists. What do we want that doesn't seem right let's see if we can we do file access dot file exists OK so if the file exists then we'll go ahead and. Set the high score frame otherwise don't do anything and we already default the value to zero so it's safe to not have an else here and we can go ahead and rerun our game. And now that works so great we found a bug and we fixed it but now let's see what the difference is between user space and res space. And I also just realized something else I want to make better so now you see in resource high score dot txt has been set and this is different. Then this high score that set to twenty and this one set to nine and this is in our project files you don't. You you don't want to save this in your project files because every time you distribute your game to players this high score value would be set already so as you're playing and testing it's going to be set and then. That will get included with your builds and then sent out unless you explicitly ignored it but it's it's not what you want right this is a different location. On my computer then this one you want to use user space for users data right it's kind of implied in the name so let's go ahead and change res back to user. And save that and I noticed another part of the code I want to make better and that is this we should use a constant. Constance like a variable but it doesn't change and we're going to call this I put them in all caps so that we know it's a constant. It's often called screaming snake case because Godot uses snake case here where it's underscores and it looks like a snake. But this is called screenings snake case and we're going to call this high score file and we're going to set this. To here. And the reason why you want to use a constant and then you can copy that paste it here you can also take it out and we'll get the auto complete. The reason why you want to use a constant here is let me undo this for a sec right you know strings are so brittle right especially if it's an important one you're referencing over and over again. What if I did that right that is never going to return true because we're never writing to that file therefore this code would never get run. And this is you don't want to repeat strings especially critical strings like this you want to put them in a variable or a constant ideally a constant because this isn't changing. Now if it was something that might change like maybe a user could set where their saved files are located maybe then it would be a variable. But for our case we want to go ahead and make it a constant. Now let's run our game make sure it still works and we'll make sure it's working in user space great it loaded 20. Let's press it a bunch and we'll break the high score barrier and go past 20 25 OK. Now we're here. That didn't get set great we'll delete that now this high score set to 25 so. Woo that was a whirlwind I feel that I'm glad we found the bug. I'm glad that we moved our code into a constant and made it better. And appreciate you learn alongside with me you know I I'm just doing these off the cuff so I sometimes forget stuff or whatever and yeah that was a good thing to learn and see. All right. Appreciate you watching hope this helped I really enjoyed making this video. Take care till next time.