Save And Load Data in Godot 3.2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
If you want your game to remember important information between play sessions, such as what levels your player has completed, their maximum health, damage they can deal, or even treasures they’ve collected. Then we’re going to need to save some data. Howdy, and welcome to the Game Endeavor. If you want to learn more practical dev skills and improve the quality of your games, then subscribe and ding-a-ling that bell. If you’ve never tried to save data before, then this topic might seem a little intimidating at first, but it’s a lot easier than you might think. Godot makes this very simple by providing us with several easy and convenient ways of managing this. For this example I’ll be demonstrating how to create binary save files that your player will not easily be able to break into and modify. Let’s get started. I have a scene prepared here for demonstrating the process of saving and loading data. This scene is very simple with two buttons that I’ll use to save and load respectively, and a text box for displaying information to you. If I click on the buttons, then they will print out to your text box the words “saved” and “loaded”, which don’t get too excited because it’s a lie. They’re not doing anything at the moment, this is just to demonstrate that the buttons are functioning properly. If we open the script to the scene then you’ll see it’s just the two methods for the buttons among other non-important things. The purpose of saving data is to store information on the user’s machine that we can access at a later date. To do this, we’re going to need to know where we want to save our data. Thankfully Godot makes this easy for us as well, by providing us with a nice and cozy little folder where we can store whatever we want, known as the “user://” directory. To easily find this, you can go into the Project tab and click `Open Project Data Folder`. You may need to run the game at least once for this folder to be generated. This folder is yours and you can stick whatever you want in here. Notice the pathing to the folder. Godot crams your little folder in with all of the other little projects that you’ve ran previously on your machine. Users will be able to see this directory path on their machine as well, so if you want to seem a little more sophisticated, then you can tell Godot that you want one of the private folders in the back. Go into Project Settings, under the config tab, and enable `Use Custom User Dir`. You’ll need to run your game so that Godot sets up the directory, but once you’ve done that, you’ll find that you have your own special directory path. This is completely optional though, and will not affect anything in this tutorial. To demonstrate saving data, we’re going to need some data to save. So right quick I’m going to create a dictionary and stuff it with some arbitrary information. You can store whatever you like here, even more dictionaries. This is useful if you want to store things like the player’s data in one dictionary, world data in another, levels completed, collectables collected, and so on. This doesn’t have to be a dictionary though, I just find that it’s a lot easier to manage, especially with larger save files. But note that it is not the most optimized way to save data because the keys will get saved in this file as well. In order to store file data, we’re going to need to create a File reference. This is what’s going to do all of the work for us. We’ll create it by saying `var file = File.new()`. We then need to open a file for us to write to, which we’re going to do by calling the method `file.open()`. This method will need two parameters. The first parameter is a string to the path that we want to open, which I’m going to create a class property for called save_path and set it to “user://save.dat”. We can then pass in `save_path` as the first parameter for our open method. We’re going to be telling Godot to write to this file. Because of this, Godot will go ahead and create the file for us, so it doesn’t need to exist already. The directory however does need to exist, so for now we’re going to save it directly in the user directory. Later on in this tutorial I will show you how to create your own directories. The second parameter is binary flags that tell Godot what we want to do with the file, the options being reading, writing, reading and writing, or writing and reading. The difference between the latter two being whether or not Godot creates the file for you. We’ll be using the flag `File.WRITE` because we want to save data to the file, we have no interest in reading it. We’ll read the data later on when the player chooses to load their game. After opening a file and reading or writing to it, we will need to tell the engine to close it so that it can flush buffers, sprinkle magic engine dust everywhere, among other fancy things. I never trust myself to remember to do this, even in a scripted tutorial, so I’m going to go ahead and call my `file.close()` method now and get it out of the way. With that crisis averted, we can focus on saving our data, which is as simple as saying `file.store_var(data)`. data is the dictionary that I created earlier with everything that I wanted to save. This is pretty painless so far. There are other methods for saving data. For example you could save 8, 16, 32, and 64 bit integers, floats, doubles, strings, variable types of variables, truly an array of things. But in my opinion, it’s convenient to store your things into dictionaries or arrays. If you write multiple things to the file then they will be saved in that order, and will need to be read in the same order that they were written. Our setup here assumes that the file opened without issues. This may not always be the case however, any number of things could go wrong while trying to open a file. The file’s open method will return an error code to inform us of how it went. You can look at Godot’s documentation to find the various error codes that you could get, if you want to try and collect them all. I’ve put a link in the description should you wish to learn more. We’re going to store our error code in a variable named `error`. We can then check to see if `error == OK` before continuing with our file handling. You’ll probably want to have an else condition here as well that handles what happens in case of an error. Such as letting your user know that something went wrong and booting them back to the previous menu. Saving data is fantastic, but completely useless unless we can also load the data back in as well, so go ahead and work on that. Inside of our loading method, we need to create the file reference again, which will be what handles the file manipulation. When loading a file, we need to make sure that the file exists by creating the condition `if file.file_exists(save_path)`, save_path referring to the string variable that we created when saving our data. We’re going to again open our file by saying `var error = file.open(save_path`, but this time using `File.READ` as our flag, so Godot knows that we want to read in data from the file. `if error == OK` then we know that the file loaded without any issues, so we’re good to start reading in data. Again I like to close my file before I get started. And then loading our data is as simple as saying `var player_data = file.get_var()`. This will load the entire dictionary that we created when saving the game and store it into a variable, which means that accessing this data is as simple as accessing the keys as you would in any other dictionary. This is the basics of saving and loading binary data, but there are a few more tricks that we can use to improve the functionality of our saving system. For example, just because you have your own little directory, doesn’t mean you want to cram each and every file into it. You may be storing multiple things here such as your save files, screenshots, bank account details. Throwing all of these into one folder can be quite messy. We can create our own directories to keep things nice and tidy. We’ll do this in our save method by saying `var dir = Directory.new()`. Much like the file reference, this will be what manages the directory for us. I’m going to create a constant string that will store the directory path by saying `const SAVE_DIR = “user://saves/`. Making sure to add the forward slash at the end, it’s rather important. We then need to insert this constant into our save_path by adding it before our save file. Back in the save method, we’ll then check to see if the directory does not exist by saying `if !dir.dir_exists(SAVE_DIR):`. If it does not exist, then we want to create it by saying `dir.make_dir_recursive(SAVE_DIR)`. The recursive bit of this method means that it will make every folder along the path if needed. For example if you were to create individual character directories inside of your save directory. If you’re confident that you’re only creating one folder, then feel free to simply use `make_dir` instead. Something we need to take a quick look at is how the data is being saved. If we open up our save file into a text editor, then most of it is not going to be shown to us properly. That’s because text editors are meant for displaying text characters, but our file has integers, floats, and other nonsense that isn’t limited to text characters. Instead, to get a better look at our file, we should open it inside of a hex editor. I will be using the free software HxD. HxD will display the file’s contents to us in various ways. On the left column we have the hexadecimal representation of our file, which is just a more human readable form of binary. And on the right column it will have the text value for us. In the data inspector on the far right, we can select values in our data file and see it converted in the inspector. If I were to select this value here, which is where I have saved my max_health value as 6. I can change this value to whatever I want, along with any other value exposed to me in this file. When I load the file in my game and print its values, my character’s max health will have been changed as a result. This means that the player will easily be able to open up this save file and manipulate their values however they want. Whether or not you want to allow this is up to you. But let us assume that you’d rather not have anyone digging around in here. We can encrypt this file so that it’s not so easy to manipulate. Back in our Godot editor, this is as simple as opening our file as encrypted by changing the `open()` method to `open_encrypted_with_pass`. This method requires a third parameter however, that being the password to encrypt the file with. This is just a string value and can be whatever you want to set it to. You will need to do this for loading the file as well. Now after saving this file again, you’ll see that the values are a jumbled mess that won’t be easy for someone to manipulate. If you want to continue learning practical dev skills, then watch this video on how to create a simple yet flexible state machine in your Godot games. And if you’re new, then join the sub-club to get notified for future videos.
Info
Channel: Game Endeavor
Views: 38,220
Rating: undefined out of 5
Keywords: Game Endeavor, GameEndeavor, rk, rk3Omega, Godot, Godot 3.0, Godot 3, Godot Engine, Godot Game Engine, Tutorial, Godot Tutorial, Godot Engine Tutorial, gdscript, 2D Game, Game Development, Game Creation, gamedev, Indie, Indie Development, Programming, Code, Coding, Scripting, save, save data, load, load data, saving and loading, binary, encryption, encrypted
Id: d0B770ZM8Ic
Channel Id: undefined
Length: 10min 7sec (607 seconds)
Published: Sat Apr 25 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.