if with almost no one, you mean non professionals who download unity and think they can make the next big instance of whatever genre is most popular atm in just a few clicks, then yes, otherwise no. i mean there is nothing bad about it, everyone needs to learn at some point, but if you fail with this, you are so deep in the learning phase, you wont create distributatble games the next couple of years.
It pains me to see that so many people have upvoted this comment. BinaryFormatter is notoriously bad and even Microsoft themselves warn NOT to use it due to its security issues. JSON is the way to go today, and really has been for years.
Quick code review ;-) * Use using statements for classes that implement IDisposable (e.g. FileStream, BinaryFormatter etc.). Like previous commenters said. * Use System.IO.Path.Combine to combine paths safely. Paths are constructed differently on different OS. This avoids a lot of potential problems. * Use a helper method to convert from Vector3 to float array or better yet, make own struct that contains 3 variables and convert between these.
@@SafaAlkan I'm not sure if you still need to know, but yes. You'd simply need to store the current stage as variable such as an int and use it just like you would any of the variables the video shows.
You’re amazing. As an artist I find your tutorials easy to follow. Thank you and your team of people. UA-cams best Unity programming specialist. I’m inspired please continue this channel.
Love it had to watch 3 different tutorials, Everyone else missed some steps, or didn't fully explain everything and i couldn't get it working. Watched this and now i can save my Gold x, Potions x a, and Apples as well as the position. Works great. Now i need to fully understand, Don't destroy on load. Thanks a lot!!
Quick tip: Look up how to use the using() statement in C# when working with unmanaged resources such as files. The good news with using the "using" statement is that the file stream will be guaranteed to be closed if you forget to do so yourself.
This is a great tutorial. I found it much more useful to use lists rather than arrays for breaking up the Vector3's, however. When you save out, you can break the V3 into separate floats, and then reassemble them on load. This has been particularly helpful for me in trying to save/load multiple groups of item data in a single file, as opposed to just a single item as would be possible with the former method. Also to note it's relatively easy to go from where he has started, to a system where you can specify save/load filenames, and read out items from a directory. If anyone is interested, I can post the code for all of that.
You should not use this anymore though. Microsoft itself is warning people from using the BinaryFormatter. From their MSDN: "The BinaryFormatter type is dangerous and is not recommended for data processing. Applications should stop using BinaryFormatter as soon as possible, even if they believe the data they're processing to be trustworthy. BinaryFormatter is insecure and can't be made secure" This video should either be taken down or have a warning.
Please, for the love of god, when you are working with a FileStream, please use a try-finally block and call "close()" in the finally clause. Otherwise, if your deserialize() fails, the file will remain open, which can lead to some super-weird and hard to debug effects.
Actually, since FileStream implements IDisposable, you could use the using statement which would effectively do the same but is cleaner. docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement
I'm building my first game using Unity and this is exactly the simple save system my game can make use of, since it's not going to be huge. Love these concise videos!
One important thing that is missing in this tutorial is to actually separate the data (PlayerData in this instance) from its FileStream cached data. In this video, it would be a function like: public PlayerData CopyPlayerData(PlayerData From){ PlayerData To = new PlayerData(); To.level = From.level; To.health = From.health; To.position = new float[3]; To.position = From.position[0]; To.position = From.position[1]; To.position = From.position[2]; return To; } The reason for doing this is to separate the data sent to the game-usable data from the Filestream data. Remember that you cannot generate an hard copy a Serializable class (PlayerData in this case) using = . An hard copy of a Serializable class is a copy cached as its own. It can only be done by using "new". So, when you do something like SerializableClass A = SerializableClass B, you're basically add a link reference from A to B. A becomes dependent of B meaning that if B cease to exist, A becomes Null and if B becomes Read-Only, A cannot be edited anymore. This is unlike something like float A = float B which makes A equal to B. Unity, and pretty much any game engine, are NOT able to generate an hard copy a Serializable Class because it ignores what's to copy. The only way to generate an hard copy (cached) of a Serializable class is to cast it as a new one. As such, SerializableClass A = new SerializableClass(); makes sure that A is an instance of a Serializable class of its own. When doing so, the class is obviously in its Default state, hence it's at that point that you want to copy its individual values (and by copying, I mean making its values equals and not just some soft copies).
Thanks man! I have watched this tutorial several times always afraid to tackle this. But now that my game has developed to the point that I need this I had to tackle it. What a surprise how easy it was. I started by making checkpoint for when my player dies and it takes me right back to the latest checkpoint. I learned a lot.
12:10 If you have it set to automatically load data, this will log an error the first time the game is played. You could of course have a load option and disable that if the save file is not found.
Watching this today so my brain can recreate this during the night, so I can simply forget everything and have to watch this again tomorrow when I'm actually recreating this
brackeys with the guardians of the galaxy soundtrack in the background while i develop a game about cats with machine guns. doesn't get much better than this
In your save chain you have the SaveSystem handling the data, it's simply passed the player. In the load chain you pass the data itself back to the Player class and let that class handle the data object. You need to pick a convention. Either pass the player to the SaveSystem.LoadPlayer function as well and have that class manage the data, or pass the already formatted PlayerData object to the SaveSystem.SavePlayer function instead of the player.
I remember doing this a year ago and attempted to implement it without understanding so much that it broke my game. Not exactly but I had to remove alot. It was working but I did something in the script after doing some of my own additions. Now I'm here again with a little bit more of knowledge.
Funny Fact: You can adapt Vector3 and even your own scripts for the formatter to save them to binary, it's a little bit longer, but its really useful when you need to save multiple objects and their positions
What makes me so happy about the way Brackeys used to teach us is the way if shoes us the flaw first and then shows us a better way to do it. Like at 9:25
There is a way to save Unity specific types like vectors, sprites, audio clips, etc. As you know, this can be done with JSON, but not be secure. So instead save the class in JSON format and then convert it into binary data. That let's us save a larger variety of fields and is nearly just as fast. Also perhaps XOR the binary with a secret key since anyone can convert binary back into original data. Or if you want to be really secure, encrypt it with AES. Good tutorial, quick and to the point. 👍
That's actually a pretty cool idea, i tried looking into AES a while back, got a few scraps of info. Mind sharing sources of info? Videos or forums idm.
Lost Conflict I made a video using AES to encrypt save data Encrypt Save Games in Unity with AES ua-cam.com/video/SXa6jSW7ckY/v-deo.html that might be of some help
If you're concerned about being reversed engineered then I suggest using encryption to your serialized data before saving, and then using decryption after de-serializing.
Easy enough to just manipulate the data once it's loaded into the RAM and then let the game save that data to your save file. Best security you're going to get with it is having it saved onto an external server so you can better control the data and how things are processed so their local end is synced with the server instead of the other way around. For many single player games, that's not really necessary unless you have systems that tie into an online system such as leaderboards/achievements.
Thank you Brackeys. I just finished my freshman year as a Computer-Engineer student. I really love programming and I am making little games (alongside from other projects) in my free time in hopes of improving my abilities. I am not extremely knowledgeable at the moment so your videos are extremely helpful. It is quite interesting to me how what I learned in University about File Handling, albeit in C and not in C#, turned out to be quite useful here. I thought Unity would use a completely different way to handle the game files. It was a nice surprise to see they are not doing that (apart from a couple of different things like _persistentDataPath). It feels really nice to know that what I am learning in University can actually be applied in what I do. I know, it is silly for me to say that, because that's what Universities are for, but sometimes it feels like I am just wasting my time there, being taught things that I will never use in everyday life.
You should use the "using() {}"-statement when working with classes implementing IDisposable. using(FileStream stream = new FileStream(path, FileMode.Open)) { // Do Something with the file } This way the file handle will be closed even if an exception is raised from the code inside the curly brackets. You don't need to call .Close() or .Dispose() yourself. Also writing a binary file might be more secure than a plain text file, but in the end, locally stored progress can always be changed, we can only make it more difficult.
For some reason it doesn't work for me... I put your lines instead of the Filestream line of Brackeys, put the PlayerData line between the brackets, then it says "Invalid initializer member declarator" on PlayerData and "Use of unassigned local var.." on stream..
I always had a habit when programming to have every single class involve MonoBehaviour in some way, it's really good knowledge that I shouldn't make every class like that. Thanks a million for the tutorial, it means a lot 🙏🙏
It's a bit more manual, and you have to update the methods every time you add data that needs to be saved/loaded, but another method I like to use is a BinaryWriter: using UnityEngine; using System; using System.IO; public static class ClassThatSavesThings { public const string PLAYER_SAVE_PATH = @"C:\The\Path\To\The.file"; // The '@' automatically escapes backslashes. public static void SavePlayer(Player player) { // Using a "using" statement causes the stream to be closed automatically when exiting the using. This is important because nothing is really written to disk until the stream is closed. using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(PLAYER_SAVE_PATH))) { writer.Write(player.level); writer.Write(player.health); writer.Write(player.transform.position.x); writer.Write(player.transform.position.y); writer.Write(player.transform.position.z); } }
public static void LoadPlayer(ref Player player) { using (BinaryReader reader = new BinaryReader(File.OpenRead(PLAYER_SAVE_PATH))) { player.level = reader.ReadInt32(); player.health = reader.ReadInt32(); player.transform = new Vector3( reader.ReadSingle(), // "Single" is just another name for "float" in this case. It's technically the other way around, but whatever. reader.ReadSingle(), reader.ReadSingle()); } } } Of course, this is a quick and slightly dirty way to do it, but it gives you tons of control over exactly how the data is formatted in binary. The main thing to watch out for is ensuring you read/write data in the same order, otherwise you'll get wrong values for sure. You can also use a BinaryWriter to write to a MemoryStream instead of a FileStream. This is great when you want to get the binary as an array of bytes (byte[]) which can be used to convert your data into Base64. I wrote this in a plain text editor, so there may be some invalid syntax.
This is neat and definitely one of the more understandable binary save systems out there so great job on that, loved the video. I do have one question, how would you go about extending this to say an inventory? While brainstorming, I thought you might be able to have a library of items to reference and store say an integer value as an id for each item to access it, however with large games like Skyrim I feel this could become tedious.
Yeah im four years late to this but for anyone with the same question now So i first defined a simple class with an id and how many you have: public class InventoryItem { public int itemID; public int Amount; } Then i make a dictionary of all the items and their corresponding Id's foreach(Item item in Items) { Items.Add(item.ItemID, item); } Then when im loading the items i can use this foreach (InventoryItem item in data.inventory) { AddItemToInventory(Items[item.itemID], item.Amount); } And in order to save the items i can do the opposite and get Ids from items In the data script I just have a list of InventoryItems: public List inventory; and just update that with the encrypted items.
I have been programming for 3 years and never have i ever used a class and the only time i used a class was to store functions and keep my code clean. This helped me lay the foundation to understanding classed
5:10 - Visual Studio has a shortcut for creating this class, simply type: "ctor" and press TAB twice and it will generate an empty constructor class, but if you have alot of properties it's better to use "CTRL + ." and select "generate constructor", this way it will bind the values to the properties of the class, and for more organization, typing "///" and TAB twice above the class it will create a commented code with the right syntax with all the properties that's the constructor takes and their implicit type, so that with IntelliSense when using the class, you just mouse over the property and with will show you a resume of the class (it also supports own comments on the class, like "Note: this class ..." Hope someone finds it useful
Just some friendly advice I was running into an IO Exception when loading or saving too quickly, Stream.Close() was taking too much time and I would eventually try and open while it was trying to close, an easy solution was using (FileStream stream = new FileStream(path, FileMode.Create)) { formatter.Serialize(stream, obj); }
This is a great tutorial! i just had one question, I am new to coding. I wanted to reset the player position when a new scene is loaded and can not seem to do it correctly. How would I go about that using this saving method?
also is there a faster way to tell the constructor what variables to assign? I have about 40 and its pretty tedious to keep typing them all out one by one.
@@ratboyOwO i know i am like 3 years late to this comment but did you ever found a solution? cuz i am making an rpg game so like there are alot of quests, enemies etc to save
10:34 If I enter player here (I use "allTimeScore" but that doesn't matter) I get the error message : The name "allTimeScore" is not available in the current context. I've been trying to find a solution for hours, I did everything the same way. I don't know what the reason is!
Nice Video, small shortcut I always use: CTRL+D will duplicate the line your cursor is actually in OR the part you have selected, instead of doing COPY+PASTE all the time when you have to duplicate anything in your code...
Make sure the function in PlayerData is the same name as the class and is NOT a public void, otherwise there will be an error in the SavePlayer function. I had to comb through my code to find that mistake xD
This doesn't work for me, for some reason my script isn't creating the file. EDIT: I fixed the problem! Basically, you cannot store your files inside of a folder, it has be the persistentDataPath and then + filename, no folders.
I freaking love your face bro! It's always so pleasant with that smile like your subconsciously encouraging us in our individual projects. Thank you for your hard work with these vids and please don't ever change.
Hi, thank you much for this tuto, I am at a point building my game where i need the player to save his progression. Unfortunally, the file is not saved... Trying to find why... (of course my scripts are identical to yours.) M. D.
Hey Brackeys, I know this is an old tutorial, but I got a question: This works just fine until I build and run my project. Everything works fine in the editor but no save information carries over when ran on build... any suggestions?
This is one of those things that makes no sense to me when I'm asked to do it, but once I'm shown how, it makes so much sense. Like, yeah obviously, how else would a save/load system work. My brain works strangely sometimes.
I would use this tutorial: www.red-gate.com/simple-talk/dotnet/c-programming/calling-restful-apis-unity3d/ And adapt it for use with the Google Sheets API here: developers.google.com/sheets/api/reference/rest/
SaveSystem should be a singleton with a single formatter instance. You should get used to using (var fs = ) {}, that way you don't have to explicitly close the FileStream. I love your tutorials, bought some courses but none explain as good as you do, keep up the good work.
Brackeys: "And now, we are ready to try it out"
Me with 5 errors: *"yes"*
@TRAGE GAMING he probably missed a lot of simple and basic stuff
me 1 error
@Anant Tiwari a lot of times we have to change code to meet our needs
@Anant Tiwari it's clear by the amount of bad spelling they are either joking, or really bad at spelling
You're lucky, but I am now with 13 errors.
"So you've made a game and it played nicely.."
Me: umm
*Syntax error on line 142*
Me: *Code ends at line 78* ?!?!?!
@@OutOfNameIdeas2 xD
VR Ready Pc Fan Me: creates script with 100 lines
C#: You have 736 errors and I won’t tell you why
@@emeralaxy850 *one missing semicolon*
@@JW-oe6nw 2 missing brackets
Oooo great one! I think this is one of the features *all* games need, but almost *no one* knows how to do it lol. Well done man! ⭐
What about the LD game with u and noa :D
I had given the suggestion of this on your channel also
if with almost no one, you mean non professionals who download unity and think they can make the next big instance of whatever genre is most popular atm in just a few clicks, then yes, otherwise no.
i mean there is nothing bad about it, everyone needs to learn at some point, but if you fail with this, you are so deep in the learning phase, you wont create distributatble games the next couple of years.
Wheres the rpg tutorial
yeah! I made clicker game but everything whould just reset
5 years in and still this is one of the best save and load system tutorial that I found out there.
It pains me to see that so many people have upvoted this comment. BinaryFormatter is notoriously bad and even Microsoft themselves warn NOT to use it due to its security issues. JSON is the way to go today, and really has been for years.
thanks for the comment. I am about to start and needed to know if it still fits.
@@imtiazsabree6562 It doesn't. DO NOT use BinaryFormatter. Microsoft themselves advise against it.
does this work for everything? like no matter what type of game you have?
same
Quick code review ;-)
* Use using statements for classes that implement IDisposable (e.g. FileStream, BinaryFormatter etc.). Like previous commenters said.
* Use System.IO.Path.Combine to combine paths safely. Paths are constructed differently on different OS. This avoids a lot of potential problems.
* Use a helper method to convert from Vector3 to float array or better yet, make own struct that contains 3 variables and convert between these.
Thanks man. Yeah, tutorials like this need review. Combine is a crucial thing to point out for OS compatibility.
Yeah I like Brackeys' videos for learning Unity but as a developer myself I recognize his code quality isn't the best
I feel a little sad watching the videos now knowing they stopped
Yeah
Yeah
İndeed :(
Yeah
He is not death yet. He just doesn't want to upload more. He still reading your comment.
It was simple enough, but also in-depth as well! Thanks!
again, seeing you in places I didn't expect
@@kodo777 I'm everywhere!! #EvilLaugh
*Does this script allow the player to resume on which stage he / she stayed in later?* Do you know?
@@SafaAlkan I'm not sure if you still need to know, but yes. You'd simply need to store the current stage as variable such as an int and use it just like you would any of the variables the video shows.
I really wish that every unity tutorial was by you, like you explain so well and it's so fun learning by you.
You’re amazing. As an artist I find your tutorials easy to follow. Thank you and your team of people. UA-cams best Unity programming specialist. I’m inspired please continue this channel.
You are the godfather of programming for me. Thank you so much for everything you do.
Did
exactly
brackeys is father
Couldn't have said it better myself
Thanks for not just going:
"Hey guys, this thing from the app store will do it all for ya.. Bai o/ "
This tutorial was actually helpfull!
hahaha good point
Love it had to watch 3 different tutorials, Everyone else missed some steps, or didn't fully explain everything and i couldn't get it working. Watched this and now i can save my Gold x, Potions x a, and Apples as well as the position. Works great. Now i need to fully understand, Don't destroy on load. Thanks a lot!!
Quick tip: Look up how to use the using() statement in C# when working with unmanaged resources such as files. The good news with using the "using" statement is that the file stream will be guaranteed to be closed if you forget to do so yourself.
Finally, after watching it for the 20th time I understood the flow after doing a dry run!! I wish Brackeys returns!!
This is a great tutorial. I found it much more useful to use lists rather than arrays for breaking up the Vector3's, however. When you save out, you can break the V3 into separate floats, and then reassemble them on load. This has been particularly helpful for me in trying to save/load multiple groups of item data in a single file, as opposed to just a single item as would be possible with the former method. Also to note it's relatively easy to go from where he has started, to a system where you can specify save/load filenames, and read out items from a directory. If anyone is interested, I can post the code for all of that.
This is perfect timing! I was about to start working on a save system over the weekend.
Why does learning stuff like this make me all giddy and excited?
You should not use this anymore though. Microsoft itself is warning people from using the BinaryFormatter. From their MSDN:
"The BinaryFormatter type is dangerous and is not recommended for data processing. Applications should stop using BinaryFormatter as soon as possible, even if they believe the data they're processing to be trustworthy. BinaryFormatter is insecure and can't be made secure"
This video should either be taken down or have a warning.
Huge shout out, Im glad that you are explaining every single line of code, it really helped me.
Please, for the love of god, when you are working with a FileStream, please use a try-finally block and call "close()" in the finally clause. Otherwise, if your deserialize() fails, the file will remain open, which can lead to some super-weird and hard to debug effects.
Actually, since FileStream implements IDisposable, you could use the using statement which would effectively do the same but is cleaner.
docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement
@@chkypros Good point, but let's not forget that this is for beginners... I'd be more than happy if they manage to use try-catch for a start.
@@AlanDarkworld Using is more beginner friendly
I am getting fucked cause of that
Can you please give an example? I don't know how to do that...
I'm building my first game using Unity and this is exactly the simple save system my game can make use of, since it's not going to be huge. Love these concise videos!
One important thing that is missing in this tutorial is to actually separate the data (PlayerData in this instance) from its FileStream cached data.
In this video, it would be a function like:
public PlayerData CopyPlayerData(PlayerData From){
PlayerData To = new PlayerData();
To.level = From.level;
To.health = From.health;
To.position = new float[3];
To.position = From.position[0];
To.position = From.position[1];
To.position = From.position[2];
return To;
}
The reason for doing this is to separate the data sent to the game-usable data from the Filestream data.
Remember that you cannot generate an hard copy a Serializable class (PlayerData in this case) using = .
An hard copy of a Serializable class is a copy cached as its own. It can only be done by using "new".
So, when you do something like SerializableClass A = SerializableClass B, you're basically add a link reference from A to B. A becomes dependent of B meaning that if B cease to exist, A becomes Null and if B becomes Read-Only, A cannot be edited anymore.
This is unlike something like float A = float B which makes A equal to B.
Unity, and pretty much any game engine, are NOT able to generate an hard copy a Serializable Class because it ignores what's to copy.
The only way to generate an hard copy (cached) of a Serializable class is to cast it as a new one. As such, SerializableClass A = new SerializableClass(); makes sure that A is an instance of a Serializable class of its own. When doing so, the class is obviously in its Default state, hence it's at that point that you want to copy its individual values (and by copying, I mean making its values equals and not just some soft copies).
Is this still the case?
Thanks man! I have watched this tutorial several times always afraid to tackle this. But now that my game has developed to the point that I need this I had to tackle it. What a surprise how easy it was. I started by making checkpoint for when my player dies and it takes me right back to the latest checkpoint. I learned a lot.
12:10 If you have it set to automatically load data, this will log an error the first time the game is played.
You could of course have a load option and disable that if the save file is not found.
15:00 add
if(data != null)
{
// Attributing the variables
}
Right if file broken or missing, fill data. Also include a Version to do compatibility checks for new data.
Watching this today so my brain can recreate this during the night, so I can simply forget everything and have to watch this again tomorrow when I'm actually recreating this
Could you maybe in the future make a tutorial on the " Easy Save " plugin you mentioned later in the video?
Nice tutorial as always, really appreciate you linking to assets and the humor and fun you bring to your vids!
Yeeee that was I needed! *thank you Brackeys*
Weebs everywhere
brackeys with the guardians of the galaxy soundtrack in the background while i develop a game about cats with machine guns. doesn't get much better than this
In your save chain you have the SaveSystem handling the data, it's simply passed the player. In the load chain you pass the data itself back to the Player class and let that class handle the data object. You need to pick a convention.
Either pass the player to the SaveSystem.LoadPlayer function as well and have that class manage the data, or pass the already formatted PlayerData object to the SaveSystem.SavePlayer function instead of the player.
I remember doing this a year ago and attempted to implement it without understanding so much that it broke my game. Not exactly but I had to remove alot. It was working but I did something in the script after doing some of my own additions.
Now I'm here again with a little bit more of knowledge.
Funny Fact: You can adapt Vector3 and even your own scripts for the formatter to save them to binary, it's a little bit longer, but its really useful when you need to save multiple objects and their positions
How i can did it?
This guy made Unity look like an app for pre-schoolers :))) I miss Brackeys!!!!
This tutorial fills me with determination.
Your health has been fully restored.
What makes me so happy about the way Brackeys used to teach us is the way if shoes us the flaw first and then shows us a better way to do it. Like at 9:25
Love your tutorials!!
Can you make a guide on in-game purchases through unity??
I also need this
One day I'm going to understand all of this, and it's going to be glorious
There is a way to save Unity specific types like vectors, sprites, audio clips, etc.
As you know, this can be done with JSON, but not be secure. So instead save the class in JSON format and then convert it into binary data. That let's us save a larger variety of fields and is nearly just as fast.
Also perhaps XOR the binary with a secret key since anyone can convert binary back into original data. Or if you want to be really secure, encrypt it with AES.
Good tutorial, quick and to the point. 👍
That's actually a pretty cool idea, i tried looking into AES a while back, got a few scraps of info. Mind sharing sources of info? Videos or forums idm.
Lost Conflict I made a video using AES to encrypt save data Encrypt Save Games in Unity with AES ua-cam.com/video/SXa6jSW7ckY/v-deo.html that might be of some help
@@ahkaay9419 unless it's server side, but that's a whole other story.
@@ahkaay9419 you can obfuscate code, and using ILCPP, the code turns native
@@sloankelly Oh ty really appreciate it.
Always coming back to this tutorial, we will miss you king! ❤
*takes newly made binary save file and converts it online to edit my save file*
Brackeys: "Am I a joke to you?"
You wanna have security? RSA
If you're concerned about being reversed engineered then I suggest using encryption to your serialized data before saving, and then using decryption after de-serializing.
Easy enough to just manipulate the data once it's loaded into the RAM and then let the game save that data to your save file. Best security you're going to get with it is having it saved onto an external server so you can better control the data and how things are processed so their local end is synced with the server instead of the other way around. For many single player games, that's not really necessary unless you have systems that tie into an online system such as leaderboards/achievements.
Thank you Brackeys. I just finished my freshman year as a Computer-Engineer student. I really love programming and I am making little games (alongside from other projects) in my free time in hopes of improving my abilities. I am not extremely knowledgeable at the moment so your videos are extremely helpful. It is quite interesting to me how what I learned in University about File Handling, albeit in C and not in C#, turned out to be quite useful here. I thought Unity would use a completely different way to handle the game files. It was a nice surprise to see they are not doing that (apart from a couple of different things like _persistentDataPath). It feels really nice to know that what I am learning in University can actually be applied in what I do. I know, it is silly for me to say that, because that's what Universities are for, but sometimes it feels like I am just wasting my time there, being taught things that I will never use in everyday life.
Hey, did you end up working in Game dev?
That momet, when you've recently finished your Saving script and a tutoriel about saving/loading by Asbjorn shows up👍
this guide saved probably many days of my life keep it up!
You should use the "using() {}"-statement when working with classes implementing IDisposable.
using(FileStream stream = new FileStream(path, FileMode.Open))
{
// Do Something with the file
}
This way the file handle will be closed even if an exception is raised from the code inside the curly brackets.
You don't need to call .Close() or .Dispose() yourself.
Also writing a binary file might be more secure than a plain text file, but in the end, locally stored progress
can always be changed, we can only make it more difficult.
For some reason it doesn't work for me...
I put your lines instead of the Filestream line of Brackeys,
put the PlayerData line between the brackets,
then it says "Invalid initializer member declarator" on PlayerData
and "Use of unassigned local var.." on stream..
@@DynaZor it is missing a bracket :
using(FileStream stream = new FileStream(path, FileMode.Open))
{
// Do Something with the file
}
I was wondering if there was a "with" keyword in C# like in python. Thanks for the tip !
*Does this script allow the player to resume on which stage he / she stayed in later?* Do you know?
then where to put the "return data " line?
I always had a habit when programming to have every single class involve MonoBehaviour in some way, it's really good knowledge that I shouldn't make every class like that.
Thanks a million for the tutorial, it means a lot 🙏🙏
16:55 the "yay" pls dont stop that
I am very excited! Super tutorial, now it's working with my invector shooter template. You are really awesome!
Your tutorials are amazing! Descriptive and clean, really suitable for beginners like us. Thanks a lot :)
ur mom
Brackeys guy, your tutorials are amazing. You make things so easy to understand. Thanks sooo much for all your great tutes.
"So I've gone ahead and set up a simple example level"
Me: uuhhmm
Finally! It took a few days, compared to the pause menu and the main menu. (NOTE: I put the save and load buttons in the pause menu.)
Hey man ur Awesome, Im ending a work of my carreer and it videos help me a lot. Congrats from Spain !
best brackeysss missed you so much come back for even vlogs only we want to see you more
It's a bit more manual, and you have to update the methods every time you add data that needs to be saved/loaded, but another method I like to use is a BinaryWriter:
using UnityEngine;
using System;
using System.IO;
public static class ClassThatSavesThings
{
public const string PLAYER_SAVE_PATH = @"C:\The\Path\To\The.file"; // The '@' automatically escapes backslashes.
public static void SavePlayer(Player player)
{
// Using a "using" statement causes the stream to be closed automatically when exiting the using.
This is important because nothing is really written to disk until the stream is closed.
using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(PLAYER_SAVE_PATH)))
{
writer.Write(player.level);
writer.Write(player.health);
writer.Write(player.transform.position.x);
writer.Write(player.transform.position.y);
writer.Write(player.transform.position.z);
}
}
public static void LoadPlayer(ref Player player)
{
using (BinaryReader reader = new BinaryReader(File.OpenRead(PLAYER_SAVE_PATH)))
{
player.level = reader.ReadInt32();
player.health = reader.ReadInt32();
player.transform = new Vector3(
reader.ReadSingle(),
// "Single" is just another name for "float" in this case. It's technically the other way around, but whatever.
reader.ReadSingle(),
reader.ReadSingle());
}
}
}
Of course, this is a quick and slightly dirty way to do it, but it gives you tons of control over exactly how the data is formatted in binary. The main thing to watch out for is ensuring you read/write data in the same order, otherwise you'll get wrong values for sure. You can also use a BinaryWriter to write to a MemoryStream instead of a FileStream. This is great when you want to get the binary as an array of bytes (byte[]) which can be used to convert your data into Base64.
I wrote this in a plain text editor, so there may be some invalid syntax.
Excellent video and description of the process. Exactly what I was needing. Keep up the great work!
"I'm gonna write fun, just to make this tutorial a bit more fun" lmao x'D. Thanks for the vids
Also I love how you're displaying the binary as hexidecimal and actually describing it as 0's and 1's but it's clearly a-f 0-9 alphanumeric
This is neat and definitely one of the more understandable binary save systems out there so great job on that, loved the video.
I do have one question, how would you go about extending this to say an inventory? While brainstorming, I thought you might be able to have a library of items to reference and store say an integer value as an id for each item to access it, however with large games like Skyrim I feel this could become tedious.
Yeah im four years late to this but for anyone with the same question now
So i first defined a simple class with an id and how many you have:
public class InventoryItem
{
public int itemID;
public int Amount;
}
Then i make a dictionary of all the items and their corresponding Id's
foreach(Item item in Items)
{
Items.Add(item.ItemID, item);
}
Then when im loading the items i can use this
foreach (InventoryItem item in data.inventory)
{
AddItemToInventory(Items[item.itemID], item.Amount);
}
And in order to save the items i can do the opposite and get Ids from items
In the data script I just have a list of InventoryItems:
public List inventory;
and just update that with the encrypted items.
I have been programming for 3 years and never have i ever used a class and the only time i used a class was to store functions and keep my code clean.
This helped me lay the foundation to understanding classed
Me - nowhere near completing my game
Also me - *I think I should add a save function*
you should. i always make it as i add content. trust me after having over 100 variables and objects (levels bosses etc) you need to save everything!
Right there with you. I have very basic stuff in and limited HUD elements with no levels or graphics even and I'm like "Yep, need a Save system"
I Like The Way That Brackeys Moves When He's Instructing Stuffs!
5:10 - Visual Studio has a shortcut for creating this class, simply type: "ctor" and press TAB twice and it will generate an empty constructor class, but if you have alot of properties it's better to use "CTRL + ." and select "generate constructor", this way it will bind the values to the properties of the class, and for more organization, typing "///" and TAB twice above the class it will create a commented code with the right syntax with all the properties that's the constructor takes and their implicit type, so that with IntelliSense when using the class, you just mouse over the property and with will show you a resume of the class (it also supports own comments on the class, like "Note: this class ..."
Hope someone finds it useful
It's amazing how much you have to compensate to achieve equality. You are a very shiny dude, keep up the good work.
Just some friendly advice I was running into an IO Exception when loading or saving too quickly, Stream.Close() was taking too much time and I would eventually try and open while it was trying to close, an easy solution was
using (FileStream stream = new FileStream(path, FileMode.Create))
{
formatter.Serialize(stream, obj);
}
Very nice video. Extremely helpful without any unnecessary additions that might potentially confuse. Thank you.
This is a great tutorial! i just had one question, I am new to coding. I wanted to reset the player position when a new scene is loaded and can not seem to do it correctly. How would I go about that using this saving method?
Its so sad Brackeys left this chanel, because it is so useful even after 2 years after the video was released.
Thank you! Perfect timing! I would really love to see how to extend this capability to AWS for cloud saving as well.
Great Tutorial! Had to redo everything because I got stuck but after that it worked perfectly.
is it me?, or does brackey sound more friendly than a Canadian
what is a brackey?
man im a canadian and all i have to say is
*ive never met anyone nicer than brackeys*
expect my dentist lol
@@V.Z.69 it's supposed to be a bracket
I had to watch this through a couple of times but I understand it much better now. thanks for the great tutorial!
also is there a faster way to tell the constructor what variables to assign? I have about 40 and its pretty tedious to keep typing them all out one by one.
@@ratboyOwO i know i am like 3 years late to this comment but did you ever found a solution? cuz i am making an rpg game so like there are alot of quests, enemies etc to save
10:34
If I enter player here (I use "allTimeScore" but that doesn't matter) I get the error message : The name "allTimeScore" is not available in the current context.
I've been trying to find a solution for hours, I did everything the same way. I don't know what the reason is!
Love your explanation for the details.
14:36, bruh i spent 5 min trying to figure out what was wrong with my code then i played the video for the next 5 seconds and proceeded to facepalm
HAHAHAH
I can't count how many times I've done that. I even gave up for a couple days on a project before because of that. lmfao.
didnt understan, what do you mean?
the life of a programmer is laced with painful inconveniences
LMAO
Nice Video, small shortcut I always use: CTRL+D will duplicate the line your cursor is actually in OR the part you have selected, instead of doing COPY+PASTE all the time when you have to duplicate anything in your code...
Make sure the function in PlayerData is the same name as the class and is NOT a public void, otherwise there will be an error in the SavePlayer function. I had to comb through my code to find that mistake xD
TYSM i had this exact problem and this fixed it
That is how you make a constructor
When I did that it gave me an error in SavePlayer script
@@MrPersonGuyPerson is your SavePlayer a script or a function? make sure its in the SaveSystem class
Thank you so much for this awesome HIGH QUALITY simple tutorial 😝 your voice is soothing lol
How would you save and load if there are multiple instances of the same class that all inherit from MonoBehaviour ??
@Ethan Bettenga I'll try that out. Thanks!
Couldn't quite understand. Can you explain more. Just a little more detailed. Thanks
@Ethan Bettenga
@Ethan Bettenga I ended up using playerperfs, but I would love to see your solution for this!
So do I
Watching these videos again even after knowing there will be no more of Brackeys videos.....
This doesn't work for me, for some reason my script isn't creating the file.
EDIT: I fixed the problem! Basically, you cannot store your files inside of a folder, it has be the persistentDataPath and then + filename, no folders.
You're an absolute legend Brackeys! Cheers!
2:48 Has no one noticed the binary file depicted actually has Hexadecimal values written on it :3
but still jokes aside Great tutorial! thanks man!
I freaking love your face bro! It's always so pleasant with that smile like your subconsciously encouraging us in our individual projects. Thank you for your hard work with these vids and please don't ever change.
Hi, thank you much for this tuto, I am at a point building my game where i need the player to save his progression.
Unfortunally, the file is not saved...
Trying to find why...
(of course my scripts are identical to yours.)
M. D.
Great tutorial ive been looking for this for quite some time!
I’ve seen Nintendo use that binary system a lot
15:20
You could write:
transform.position = new Vector3(data.position[0], data.position[1], data.position[2]);
is there a way to save the scene too?
well yes but actually no
This improved my game 10x times! Thank you!
Hey Brackeys, I know this is an old tutorial, but I got a question:
This works just fine until I build and run my project. Everything works fine in the editor but no save information carries over when ran on build... any suggestions?
i think Application.persistentDataPath returns a different path when buildt, this is so your editor save is separate from your buildt game save.
Have you found the solution for this? I have the same issue.
Even if brackeys isn't the top of the search, you find his video anyway
Hi, what if the load button is in another scene. what should i do?
I create an an object that wont be destroyed and inside that i condition something if condition meet i put my load in my characterstats awake.
What did you do? I really need to know
@@marjulbalcon3128 i need to know :D
This is one of those things that makes no sense to me when I'm asked to do it, but once I'm shown how, it makes so much sense. Like, yeah obviously, how else would a save/load system work. My brain works strangely sometimes.
Can you make a tutorial how to input data to google spreadsheet in unity and use the data from the google spreadsheet in unity?
I would use this tutorial: www.red-gate.com/simple-talk/dotnet/c-programming/calling-restful-apis-unity3d/
And adapt it for use with the Google Sheets API here: developers.google.com/sheets/api/reference/rest/
SaveSystem should be a singleton with a single formatter instance.
You should get used to using (var fs = ) {}, that way you don't have to explicitly close the FileStream.
I love your tutorials, bought some courses but none explain as good as you do, keep up the good work.
YEEESSSSSS FINALLY
det tog dig lidt for lang tid :D
I'm a simple man. I see Danganronpa, I like
@@zuccdanelia Same here
Tog väldigt lång tid
Awesome tutorial man, thank you so much!
Show us how to make a fog of war effect for an rts game.
Doesn't seem that much complicated. Sounds like just checking if in your view area then light up and make enemy unit visible.
You good bro?
I'd like to use it for a map, sort of like the map in Minecraft. Starts off blank but gets revealed as the player passes through.
brother,
file handling in c# ke concept sikhane ke baare me bhi sochna - PLEASE 🙏🙏
how do you do load a save/game if there isn't one to begin with?
You don't, it will just return null. that was what the else{ return = null} thing was in the LoadPlayer method , I believe. But I'm pretty new to this
BTW if you want to delete a file use file.delete (path)