Hi everyone! Happy Sunday! Most of the time I prefer to bind my save/load data instead of wasting time trying to find all the savable objects when I need them. Let me know how you approach data persitence in your games! I've added support for saving Scriptable Object references to the repository as well (was not covered in the video).
I couldn't figure it out by myself, there is instanceID at 17:55 but I couldn't find that in ItemDetails scriptable object on your repository. I'm guessing it is simply an ID to get the details from a collection but it looks like you don't have one in this demo scene hierarchy ?
Seriously, you keep producing the most high quality Unity content TH-cam has ever seen. :) As a Software Developer doing gamdev as a hobby I appreciate this approach to gamedev.
Agreed 100%. This content is insane, it's above the level of most paid courses as well, I still can't believe we get something this good on youtube. Git-amend is a true hero.
Every single video is top tier. Thank you for these amazing videos. I hope you can make enough money from pure teaching. I would genuinely love to learn from you. I think teaching is the most respectable thing a human being can do for others so others can take it, add on top of it and pass it to the future for common knowledge and progress of whole humanity. We wouldn't be here if some people who figured how some things work kept this knowledge for themselves instead of taking time to share this knowledge to others. Cause most people do keep it for themselves and that knowledge dies when they die. Imagine every generation has to reinvent electricity and computers from scratch. So thank you for doing this and i hope you can keep doing this.
I really appreciate the consistent high quality content. You’ve inspired me to start learning game architecture. Do you have any videos where you talk about your journey of becoming a senior engineer?
Thanks for this, will dive into this soon. I have a save system but looking to improve the one I got. Creating the kind of game that I'm doing now requires so much saving :D 🙏 Thank you
My friend - keep it up. I'm not the best in this "Software development" thing but I can clearly see what an ocean of difference your vids make. I've always wanted to decouple my code (Which you cover in a different vid) and I didn't know how. It seems so daunting and crazy - those abstractions - but I have some hope for it now.
Nice!! Was just about to purchase a save/load asset - gonna watch this first so I can decide whether it's viable/worth it to make one by myself. Thanks!!
Great tutorial! Thanks! I definitely learned few new tricks. I spend some time messing with the system, but there is one thing that's kept bugging me out. Pun unintended. When we call "Bind" method of the SaveLoadSystem class, we passing there some TData that contains within GameData class. In Bind method we checking if TData null. And if it is, we create new TData. But it seems like this null check is rather pointless because GameData class won't reference this new instance of TData created by "Bind" method. Only object with IBind interface will receive it. In this case that, pretty much, will break the whole thing. GameData won't receive any changes that happened on IBind object. Of course this won't happen when all TData in the GameData class created in inspector. Since both TData and GameData marked as SerializeField. But then it means that this null check will never happen. And i can't quite figure out why then this check needed? Am i missing something important here? I stepped on this rake because decided to use SerializeReference instead of SerializeField. In my case that was necessary to make sure that GameData is created and managed only in runtime.
for those unable to get the init keyword to compile. There's a bug in visual studio 2019 where you have to make a dummy class for it to compile namespace System.Runtime.CompilerServices { internal static class IsExternalInit {} }
Great tutorial! How do you handle versioning? What if GameData changes in a new version of the game? The Serializer will fail to read an old save file. For bigger, breaking changes this is of course fine, but for smaller adjustments I don't want my players to lose all progress due to a small addition of one persistent value.
Thanks for the comment! I'm sure other people will have similar questions. First of all, make sure to start adding a version number as soon as you think you are going to have to maintain multiple versions of saved data - some kind of id system, even if it's just an integer. After that, the easiest thing to do is to implement a migration strategy. When you deserialize using Unity's Json library, missing fields in your saved data (the new fields in the GameData class) are set to the default values. Check the version number and then handle migration if you need to set those new fields to something special.
What are the pros and cons of saving as a single json dump versus having multiple files for each of your game data? Feels like having inventory, player and game data separate would be more scalable and more resistant to file save corruption.
That's an interesting question. I would imagine the main pro of one file is simplicity. There would also only be only one read or write operation performed, which might be an advantage in some scenarios. The flip side of course are advantages you've already pointed out, and beyond that you might want to be able to save data more frequently for some game objects than for others, depending on your game. I think it really comes down to a per project level, but definitely something to consider.
Had to implement something similar, as my use of assemblies did not allow for having one concrete GameData class. What I did was have each SaveSlot as a folder, and then one file for each ISaveable.
Great video tutorial, but I want to ask a question about future possible multi line Bind method calls in SaveLoadSystem.OnSceneLoaded. Could we instead try to create a public events like "OnLoad" and "OnSave" and just let each ISaveable class to react and write/read his own data part when event is dispatched?
You could do that, but it would defeat the purpose of binding data as shown in this video. Most naïve systems do exactly what you are describing - the trade off is that you then have to have each of these ISaveable classes register and deregister for events and implement their own save/load logic which could become cumbersome. You then have to decide if you want each entity to save into it's own data file or combine them all somehow.
I have another plan already for next week - but a video about an audio system would be a good topic. What kind of features would you want to include in such a system?
@@git-amend I feel like I don't pay enough attention to sound in the games I make. In the last game I made, I tried to do something by combining your flyweight factory with the audio system, but I messed up. It would be perfect if you made a sound system video where we can use Audio Sources in a more optimized way.
That's called Better Transform by Tiny Giant Studio, they also have an improvement for the Mesh Renderer component as well: assetstore.unity.com/packages/tools/utilities/better-transform-real-size-global-local-switch-and-child-parent--258314
I really love your videos! I have a question: how would you save and prevent loading something that's already been destroyed in the scene, such as an enemy that was killed? Currently, when I stop and start the editor and load the game, the dead enemies are back to their last saved positions.
Well, that's a difficult question to answer without seeing your entire project. If you are following along with the video, you'll notice there is a Bind method that can handle Lists of things for saving, and that could include a list of Enemies and their positions. You would need to tie this into your spawning system, so that as you spawn enemies into (and out of) your game you keep this list up to date. Then when the game is saved you should be able to save a list of all the enemy positions on the level and when the game starts up your spawning system can put them back into the right places, potentially with all the same data. You would do the same thing for items and so on.
may I ask about the other use cases of the SerializableGuid class? I can infer its use to compare objects by Guid and to load all resources to an int lookup table, but I feel like it shouldn't need to be this complicated if it does not have more uses. If the item map gets big enough for lookups to be slow, then I probably need to load less instead of using ints. Resources.Load also caches the first call so the next calls are faster until you release them, so I would prefer to call it once for initialization then keep calling it instead of storing it in an extra dictionary. If performance is still an issue, I'd prefer to cache the resource on the using classes instead of a central data map. I also don't think guid comparisons are gonna be used a lot. What are your thoughts?
Thanks for the comment! The SerializableGuid class is just an example of how you can serialize a Guid. I personally don't find it to be complicated, but of course you should implement whatever identification system is suitable for you and your project. As for optimizations of loading resources and so on, those are important things, but they are topics for another video. I wouldn't typically use Resources.Load, however I knew that if I did not add something to the repository, people would complain. We'll have a video about Asset Bundles and Addressables in the near future, and address some of these concerns about loading and memory. Cheers!
How would you recommend I use this to save data for multiple levels? For example if I have 8 levels and I want to save the highscore for each, how many stars the player has, what levels still need to be unlocked, etc. I was thinking of using a dictionary but I'm not sure how that would work using the data binding system.
For more complex types that aren't supported out of the box with Unity's built in tools, most people will use the Newtonsoft-JSON package. Check it out here: docs.unity3d.com/Packages/com.unity.nuget.newtonsoft-json@3.0/manual/index.html Specific Dictionary example: www.newtonsoft.com/json/help/html/SerializeDictionary.htm
Sadly for me the property [field: SerializeField] public SerializableGuid Id { get; set; } = SerializableGuid.NewGuid(); and [field: SerializeField] public SerializableGuid Id { get; set; } do not serialize. I'm using latest unity version and still investigating the issue but for now it does not work for me 2022.3.16
@@git-amend thanks for the quick reply. Indeed, it seems the issue was with the SerializableGuidDrawer class to showcase it properly. I'll continue testing your approach. Just cloning the repo itself is not possible since it has dependencies and missing entities. But for now all good :)
@@git-amend I encountered another problem (just for me I hope). When I start playing it goes to the default persistent property state. I did everything as in the tutorial of yours (This is my hero): public void Bind(PlayerData data) { _playerData = data; _playerData.Id = Id; transform.position = data.Position; } private void Update() { _playerData.Position = transform.position; } And it goes always transform.position = data.Position; (in your video it does not for some reason) but I wanna it to happen only on load. If I fix I'll let you know. P.S. Long story short on each play it goes to default position of the data I store in GameData The way I had to do it is the following but you in your videos did not have this check and I can not wrap my head around why: public void Bind(PlayerData data) { _playerData = data; _playerData.Id = Id; // Check if the loaded position is not the default Vector3(0,0,0) or any other invalid position if (_playerData.Position != Vector3.zero) { transform.position = _playerData.Position; } else { // Optionally, set a default starting position here // transform.position = defaultStartingPosition; } } Maybe for you this check works if (data == null) { data = new TData { Id = entity.Id }; } but for me it never does since GameData always exists
I think your hero is also sent to (0,0,0) :) I rewatched your video many times and that's the most logical conclusion I can get. This is on timestep 12:36
Something I couldnt get in the video: Ok, you save an ID for the items, but how do you load the Scriptable objects based on the ID? Do you have an instance of a list that has all the items of the game and then you search the id inside this list?
If you take a look in the repository, there is an extra commit made after this video's code that adds support for scriptable objects save/load. It is fairly straight forward.
There could be many reasons for this, so this question can't be answered without more information. You'll have to compare your code carefully to what's in the repository.
Sure, you don't have to wrap any of the data in the GameData object - it could all be separate if you wanted to. The main wrapper is more of a convenience object, and would make it a bit easier if you needed to start versioning for example.
How do I need to change the structure of this when there are multiple inventories? This implementation only lets me register a type once, say its like minecraft and theres hundreds of chests all with their own inventory data structure, should I just make the thing thats being saved a collection of InventoryData or is there a better solution?
In that case you might want to start using the Bind(List datas) method so that you can associate each unique Inventory with a matching Id (the Id denoting the owner of the Inventory).
Instance Ids are liable to change. The system can be improved by using the SerializeableGuid of each asset instead. I've added another commit to the repository to show how that can be done.
Your method to save the inventory (using instanceID) will not work outside the editor. Even if you save and reload the unity editor and load it won't work. One way is to use sort of database holding the item prefabs ids
@@git-amend Thx, i will look into it. What you also need to implement is deleting objects from scene after loading if they were deleted before. What i do in my system is, after loading, delete everything which saveable in the scene and create everything from the save file.
Sure! Unity supports Base64 out-of-the-box - check out the end of this article for how to use it: pudding-entertainment.medium.com/unity-how-to-save-and-load-files-using-json-and-base64-a033def09a47
8 หลายเดือนก่อน
This might be my only hope for making a save system like Half-Life
Great video, but I have a question, Where do we update the GameData in the save system? I see that in the hero class we update the data position and rotation, but this data is only stored for the player, in which moment the saveSystem gets this information? The save method only save its own game data
The only issue is. the GUID class in c# can produce duplicate GUIDs even though it is very low odds. Which makes it a bit problematic and could occur randomly in the future without warning. Why not just have a simple integer counter which guarantees no duplicates ? You would simply have to also serialize the counter number to continue on where you left off but this would guarantee no duplicate unique ids. Their article on it says " Such an identifier has a very low probability of being duplicated." but its not zero. But an int counter eliminates the problem.
Thank you so much for this tutorial, it's really easy to understand and I feel like I learnt a lot!! (also about how to design systems before implementing them, which is always difficult for me... hahaha) I have a question about an issue I'm having when implementing the ISaveable interface: The "SerializableGuid Id" added has the "init;" keyword as well, which does not allow to later assign the Id of the MonoBehavior in the "Bind" implementation. But if I try to change the keyword to "set;" (which I later noticed you have it like this in the video) it gives me this message: 'SerializableGuid gachapon.Data.PlayerData.Id' must have 'init' accessor instead of 'set' to implement property 'SerializableGuid gachapon.Systems.Persistence.ISaveable.Id' I also added the "IsExternalInit" workaround because it was giving me an error, should I just change the ISaveable interface to use "set;" instead? (thank you again, and sorry for the long comment!)
Yes, you can just use `set` instead, in fact it's been changed in the repository already based on subscriber feedback - and discussed on Discord, feel free to join, you'll find a lot of answers there to things like this and more!
Hi everyone! Happy Sunday! Most of the time I prefer to bind my save/load data instead of wasting time trying to find all the savable objects when I need them. Let me know how you approach data persitence in your games! I've added support for saving Scriptable Object references to the repository as well (was not covered in the video).
I couldn't figure it out by myself, there is instanceID at 17:55 but I couldn't find that in ItemDetails scriptable object on your repository.
I'm guessing it is simply an ID to get the details from a collection but it looks like you don't have one in this demo scene hierarchy ?
@@ivvo7002 scriptable objects are usually found in assets, not in scene. they are a kinda data containers.
Seriously, you keep producing the most high quality Unity content TH-cam has ever seen. :) As a Software Developer doing gamdev as a hobby I appreciate this approach to gamedev.
Thanks for the comment! Lots more to come!
Agreed 100%. This content is insane, it's above the level of most paid courses as well, I still can't believe we get something this good on youtube. Git-amend is a true hero.
same here :)
I, for one, welcome our new Unity tutorial overlords.
So far it's the only unity dev channel that actually codes properly. The last was infallable code but they stoped posting.
This is ABSOLUTE expertise and professionalism
Too kind!
Incredibly informative to see how to properly set something like this up. I can’t wait to watch the rest of your videos. Thank you!
Awesome, thank you!
I hope you get more exposure. your videos are really good!
Thank you so much!
Every single video is top tier. Thank you for these amazing videos. I hope you can make enough money from pure teaching. I would genuinely love to learn from you. I think teaching is the most respectable thing a human being can do for others so others can take it, add on top of it and pass it to the future for common knowledge and progress of whole humanity. We wouldn't be here if some people who figured how some things work kept this knowledge for themselves instead of taking time to share this knowledge to others. Cause most people do keep it for themselves and that knowledge dies when they die. Imagine every generation has to reinvent electricity and computers from scratch. So thank you for doing this and i hope you can keep doing this.
Thanks so much! I really appreciate the comment.
I really appreciate the consistent high quality content. You’ve inspired me to start learning game architecture. Do you have any videos where you talk about your journey of becoming a senior engineer?
I don't yet, but that is a very requested video. Sometime this year I will make a video like that. Thanks!
@@git-amend looking forward for this one too!!
Top quality with good code, love it man!
Much appreciated!
Thanks for this, will dive into this soon. I have a save system but looking to improve the one I got. Creating the kind of game that I'm doing now requires so much saving :D 🙏 Thank you
Good luck!
Really high quality content!!
Thanks!
My friend - keep it up.
I'm not the best in this "Software development" thing but I can clearly see what an ocean of difference your vids make.
I've always wanted to decouple my code (Which you cover in a different vid) and I didn't know how. It seems so daunting and crazy - those abstractions - but I have some hope for it now.
Thanks, will do! Appreciate the comment!
Nice!! Was just about to purchase a save/load asset - gonna watch this first so I can decide whether it's viable/worth it to make one by myself. Thanks!!
Great! Some of the paid Assets are very good, let me know if you buy one!
Great tutorial! Thanks! I definitely learned few new tricks. I spend some time messing with the system, but there is one thing that's kept bugging me out. Pun unintended.
When we call "Bind" method of the SaveLoadSystem class, we passing there some TData that contains within GameData class. In Bind method we checking if TData null. And if it is, we create new TData. But it seems like this null check is rather pointless because GameData class won't reference this new instance of TData created by "Bind" method. Only object with IBind interface will receive it. In this case that, pretty much, will break the whole thing. GameData won't receive any changes that happened on IBind object. Of course this won't happen when all TData in the GameData class created in inspector. Since both TData and GameData marked as SerializeField. But then it means that this null check will never happen. And i can't quite figure out why then this check needed? Am i missing something important here?
I stepped on this rake because decided to use SerializeReference instead of SerializeField. In my case that was necessary to make sure that GameData is created and managed only in runtime.
I can finally understand this. Now I'm building a Save Load System
Nice work, keep it up!
@@git-amend thanks!
for those unable to get the init keyword to compile. There's a bug in visual studio 2019 where you have to make a dummy class for it to compile
namespace System.Runtime.CompilerServices
{
internal static class IsExternalInit {}
}
Interesting, thanks for sharing that.
Great tutorial! How do you handle versioning? What if GameData changes in a new version of the game? The Serializer will fail to read an old save file. For bigger, breaking changes this is of course fine, but for smaller adjustments I don't want my players to lose all progress due to a small addition of one persistent value.
Thanks for the comment! I'm sure other people will have similar questions. First of all, make sure to start adding a version number as soon as you think you are going to have to maintain multiple versions of saved data - some kind of id system, even if it's just an integer.
After that, the easiest thing to do is to implement a migration strategy. When you deserialize using Unity's Json library, missing fields in your saved data (the new fields in the GameData class) are set to the default values. Check the version number and then handle migration if you need to set those new fields to something special.
@@git-amend Thanks for your answer, that's helpful!
What's that whiteboard app you're using to explain the structure of the system? It makes the concept so much easier for me to understand that way!
That is Excalidraw for Obsidian th-cam.com/video/P_Q6avJGoWI/w-d-xo.html
What are the pros and cons of saving as a single json dump versus having multiple files for each of your game data? Feels like having inventory, player and game data separate would be more scalable and more resistant to file save corruption.
That's an interesting question. I would imagine the main pro of one file is simplicity. There would also only be only one read or write operation performed, which might be an advantage in some scenarios. The flip side of course are advantages you've already pointed out, and beyond that you might want to be able to save data more frequently for some game objects than for others, depending on your game. I think it really comes down to a per project level, but definitely something to consider.
Had to implement something similar, as my use of assemblies did not allow for having one concrete GameData class. What I did was have each SaveSlot as a folder, and then one file for each ISaveable.
not sure if you've mentioned this elsewhere, what is that drawing software you're using to visualize architecture?
That's Excalidraw for Obsidian. You can also use the web version: excalidraw.com/
th-cam.com/video/o0exK-xFP3k/w-d-xo.html
He has tried a few in the past. I don't know which one he is using here. Might be listed in the Discord.
Great video tutorial, but I want to ask a question about future possible multi line Bind method calls in SaveLoadSystem.OnSceneLoaded. Could we instead try to create a public events like "OnLoad" and "OnSave" and just let each ISaveable class to react and write/read his own data part when event is dispatched?
You could do that, but it would defeat the purpose of binding data as shown in this video. Most naïve systems do exactly what you are describing - the trade off is that you then have to have each of these ISaveable classes register and deregister for events and implement their own save/load logic which could become cumbersome. You then have to decide if you want each entity to save into it's own data file or combine them all somehow.
Can you make a video about Audio System for next week?
By the way, this week's topic is very interesting as always. Thank you very much for making videos with such high quality content.
I have another plan already for next week - but a video about an audio system would be a good topic. What kind of features would you want to include in such a system?
@@git-amend I feel like I don't pay enough attention to sound in the games I make. In the last game I made, I tried to do something by combining your flyweight factory with the audio system, but I messed up. It would be perfect if you made a sound system video where we can use Audio Sources in a more optimized way.
it feels criminal to watch to watch this content for free
Thanks for the comment, made my day!
Who would buy if git amend make a premium course ? Me first.
Thanks for the vote of confidence!
Hello sir. Thanks for the nice tutorial. I have a question. How did you create that custom editor for the Transform component? Is it a package?
That's called Better Transform by Tiny Giant Studio, they also have an improvement for the Mesh Renderer component as well:
assetstore.unity.com/packages/tools/utilities/better-transform-real-size-global-local-switch-and-child-parent--258314
Thank you so much
I really love your videos! I have a question: how would you save and prevent loading something that's already been destroyed in the scene, such as an enemy that was killed? Currently, when I stop and start the editor and load the game, the dead enemies are back to their last saved positions.
Well, that's a difficult question to answer without seeing your entire project. If you are following along with the video, you'll notice there is a Bind method that can handle Lists of things for saving, and that could include a list of Enemies and their positions. You would need to tie this into your spawning system, so that as you spawn enemies into (and out of) your game you keep this list up to date. Then when the game is saved you should be able to save a list of all the enemy positions on the level and when the game starts up your spawning system can put them back into the right places, potentially with all the same data. You would do the same thing for items and so on.
I see, I'll try that out! Thanks for the feedback, I think this is the answer I'm looking for!
may I ask about the other use cases of the SerializableGuid class? I can infer its use to compare objects by Guid and to load all resources to an int lookup table, but I feel like it shouldn't need to be this complicated if it does not have more uses. If the item map gets big enough for lookups to be slow, then I probably need to load less instead of using ints. Resources.Load also caches the first call so the next calls are faster until you release them, so I would prefer to call it once for initialization then keep calling it instead of storing it in an extra dictionary. If performance is still an issue, I'd prefer to cache the resource on the using classes instead of a central data map. I also don't think guid comparisons are gonna be used a lot. What are your thoughts?
great video btw! Another immediate watch for me.
Thanks for the comment! The SerializableGuid class is just an example of how you can serialize a Guid. I personally don't find it to be complicated, but of course you should implement whatever identification system is suitable for you and your project. As for optimizations of loading resources and so on, those are important things, but they are topics for another video. I wouldn't typically use Resources.Load, however I knew that if I did not add something to the repository, people would complain. We'll have a video about Asset Bundles and Addressables in the near future, and address some of these concerns about loading and memory. Cheers!
How would you recommend I use this to save data for multiple levels? For example if I have 8 levels and I want to save the highscore for each, how many stars the player has, what levels still need to be unlocked, etc. I was thinking of using a dictionary but I'm not sure how that would work using the data binding system.
For more complex types that aren't supported out of the box with Unity's built in tools, most people will use the Newtonsoft-JSON package. Check it out here: docs.unity3d.com/Packages/com.unity.nuget.newtonsoft-json@3.0/manual/index.html
Specific Dictionary example: www.newtonsoft.com/json/help/html/SerializeDictionary.htm
@@git-amend Awesome thank you so much for the reply, I appreciate it.
Sadly for me the property [field: SerializeField] public SerializableGuid Id { get; set; } = SerializableGuid.NewGuid(); and [field: SerializeField] public SerializableGuid Id { get; set; } do not serialize. I'm using latest unity version and still investigating the issue but for now it does not work for me 2022.3.16
Are you using the PropertyDrawer from the repository?
@@git-amend thanks for the quick reply. Indeed, it seems the issue was with the SerializableGuidDrawer class to showcase it properly. I'll continue testing your approach. Just cloning the repo itself is not possible since it has dependencies and missing entities. But for now all good :)
@@git-amend I encountered another problem (just for me I hope). When I start playing it goes to the default persistent property state.
I did everything as in the tutorial of yours (This is my hero):
public void Bind(PlayerData data)
{
_playerData = data;
_playerData.Id = Id;
transform.position = data.Position;
}
private void Update()
{
_playerData.Position = transform.position;
}
And it goes always transform.position = data.Position; (in your video it does not for some reason) but I wanna it to happen only on load. If I fix I'll let you know.
P.S. Long story short on each play it goes to default position of the data I store in GameData
The way I had to do it is the following but you in your videos did not have this check and I can not wrap my head around why:
public void Bind(PlayerData data)
{
_playerData = data;
_playerData.Id = Id;
// Check if the loaded position is not the default Vector3(0,0,0) or any other invalid position
if (_playerData.Position != Vector3.zero)
{
transform.position = _playerData.Position;
}
else
{
// Optionally, set a default starting position here
// transform.position = defaultStartingPosition;
}
}
Maybe for you this check works
if (data == null) {
data = new TData { Id = entity.Id };
}
but for me it never does since GameData always exists
I think your hero is also sent to (0,0,0) :) I rewatched your video many times and that's the most logical conclusion I can get. This is on timestep 12:36
@@nanaschi Did you ever find a solution to things going back to (0,0,0)? I'm having the same problem (mine is 0,0 since i'm working on a 2d game)
Something I couldnt get in the video: Ok, you save an ID for the items, but how do you load the Scriptable objects based on the ID? Do you have an instance of a list that has all the items of the game and then you search the id inside this list?
If you take a look in the repository, there is an extra commit made after this video's code that adds support for scriptable objects save/load. It is fairly straight forward.
Wonderful!! But can I save a dictionary?
You will probably want to look into a solution for serializing Dictionaries or create your own. There are some free ones on the asset store / GitHub.
ISaveable only lets me init and not set. I am using the drawer from the repository as well. How can I fix it?
There could be many reasons for this, so this question can't be answered without more information. You'll have to compare your code carefully to what's in the repository.
@@git-amend Thanks for the quick reply! I'll look into it. Great video!!
Would it be possible to have many different GameData instances to save some content in different files ?
Sure, you don't have to wrap any of the data in the GameData object - it could all be separate if you wanted to. The main wrapper is more of a convenience object, and would make it a bit easier if you needed to start versioning for example.
@@git-amend Thank you !! Great video btw
What's the name of the program used to make the diagrams? It looks really clean and simple.
That's Excalidraw for Obsidian. You can also use the web version: excalidraw.com/
th-cam.com/video/o0exK-xFP3k/w-d-xo.html
@@git-amend Thank you!
@@git-amendThanks a lot!
Perfection
Thanks!
How do I need to change the structure of this when there are multiple inventories? This implementation only lets me register a type once, say its like minecraft and theres hundreds of chests all with their own inventory data structure, should I just make the thing thats being saved a collection of InventoryData or is there a better solution?
In that case you might want to start using the Bind(List datas) method so that you can associate each unique Inventory with a matching Id (the Id denoting the owner of the Inventory).
so itd be like Bind ?@@git-amend
Is it safe to persist the instance ID of the scriptable objects for items? Are they not liable to change?
Instance Ids are liable to change. The system can be improved by using the SerializeableGuid of each asset instead. I've added another commit to the repository to show how that can be done.
Nice.
Thank you! Cheers!
Your method to save the inventory (using instanceID) will not work outside the editor. Even if you save and reload the unity editor and load it won't work. One way is to use sort of database holding the item prefabs ids
Very astute. As mentioned in the pinned comment and description, you can see an implementation in the repository.
@@git-amend Thx, i will look into it. What you also need to implement is deleting objects from scene after loading if they were deleted before. What i do in my system is, after loading, delete everything which saveable in the scene and create everything from the save file.
what is that flow diagram you are using sir
That is ExcaliDraw for ObsidianØ
th-cam.com/video/o0exK-xFP3k/w-d-xo.html
Greetings! Is there a way to somehow encrypt the file so that a dishonest player can't just find it, open it, and give himself 999 mushrooms?
Sure! Unity supports Base64 out-of-the-box - check out the end of this article for how to use it:
pudding-entertainment.medium.com/unity-how-to-save-and-load-files-using-json-and-base64-a033def09a47
This might be my only hope for making a save system like Half-Life
Great, I hope it inspires you to build something awesome!
Great video, but I have a question, Where do we update the GameData in the save system? I see that in the hero class we update the data position and rotation, but this data is only stored for the player, in which moment the saveSystem gets this information?
The save method only save its own game data
The only issue is. the GUID class in c# can produce duplicate GUIDs even though it is very low odds. Which makes it a bit problematic and could occur randomly in the future without warning. Why not just have a simple integer counter which guarantees no duplicates ? You would simply have to also serialize the counter number to continue on where you left off but this would guarantee no duplicate unique ids. Their article on it says " Such an identifier has a very low probability of being duplicated." but its not zero. But an int counter eliminates the problem.
Look up how it works. The odds are for all practical purposes zero.
Can you give more instructions on how to encrypt files to avoid thieves?
Thank you so much for this tutorial, it's really easy to understand and I feel like I learnt a lot!! (also about how to design systems before implementing them, which is always difficult for me... hahaha)
I have a question about an issue I'm having when implementing the ISaveable interface:
The "SerializableGuid Id" added has the "init;" keyword as well, which does not allow to later assign the Id of the MonoBehavior in the "Bind" implementation.
But if I try to change the keyword to "set;" (which I later noticed you have it like this in the video) it gives me this message:
'SerializableGuid gachapon.Data.PlayerData.Id' must have 'init' accessor instead of 'set' to implement property 'SerializableGuid gachapon.Systems.Persistence.ISaveable.Id'
I also added the "IsExternalInit" workaround because it was giving me an error, should I just change the ISaveable interface to use "set;" instead?
(thank you again, and sorry for the long comment!)
Yes, you can just use `set` instead, in fact it's been changed in the repository already based on subscriber feedback - and discussed on Discord, feel free to join, you'll find a lot of answers there to things like this and more!
@@git-amend Ohh, thank you so much!! I will try to join asap!
Is it as simple as a few words to save everything, or do you have to identify every little thing to save everything each?
Do mean in as far as determining what to save and binding the data, or performing an actual save of the data?
A script to save and load everything at once instead of identifying every little thing I want