No video

Better Save/Load using Data Binding in Unity

  Рет қаралды 12,747

git-amend

git-amend

Күн бұрын

Пікірлер: 117
@git-amend
@git-amend 6 ай бұрын
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).
@johanlindberg628
@johanlindberg628 6 ай бұрын
Seriously, you keep producing the most high quality Unity content KZfaq has ever seen. :) As a Software Developer doing gamdev as a hobby I appreciate this approach to gamedev.
@git-amend
@git-amend 6 ай бұрын
Thanks for the comment! Lots more to come!
@jacobs.7925
@jacobs.7925 6 ай бұрын
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.
@DadMakesGamez
@DadMakesGamez 6 ай бұрын
same here :)
@martinleissler8329
@martinleissler8329 6 ай бұрын
I, for one, welcome our new Unity tutorial overlords.
@simonnordon8421
@simonnordon8421 6 ай бұрын
So far it's the only unity dev channel that actually codes properly. The last was infallable code but they stoped posting.
@qwetzq9757
@qwetzq9757 6 ай бұрын
This is ABSOLUTE expertise and professionalism
@git-amend
@git-amend 6 ай бұрын
Too kind!
@madpelican9242
@madpelican9242 3 ай бұрын
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.
@KuroUsagi1010
@KuroUsagi1010 6 ай бұрын
I hope you get more exposure. your videos are really good!
@git-amend
@git-amend 6 ай бұрын
Thank you so much!
@Niyazi-01
@Niyazi-01 5 ай бұрын
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.
@git-amend
@git-amend 5 ай бұрын
Thanks so much! I really appreciate the comment.
@renegaderaid
@renegaderaid 5 ай бұрын
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!
@git-amend
@git-amend 5 ай бұрын
Awesome, thank you!
@rashadsworld_
@rashadsworld_ 6 ай бұрын
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?
@git-amend
@git-amend 6 ай бұрын
I don't yet, but that is a very requested video. Sometime this year I will make a video like that. Thanks!
@charfractal9441
@charfractal9441 12 күн бұрын
@@git-amend looking forward for this one too!!
@jacobs.7925
@jacobs.7925 6 ай бұрын
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!!
@git-amend
@git-amend 6 ай бұрын
Great! Some of the paid Assets are very good, let me know if you buy one!
@radiantgames55
@radiantgames55 6 ай бұрын
Really high quality content!!
@git-amend
@git-amend 6 ай бұрын
Thanks!
@nc956
@nc956 5 ай бұрын
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.
@git-amend
@git-amend 5 ай бұрын
Thanks, will do! Appreciate the comment!
@metaling278
@metaling278 5 ай бұрын
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 {} }
@git-amend
@git-amend 5 ай бұрын
Interesting, thanks for sharing that.
@charfractal9441
@charfractal9441 14 күн бұрын
it feels criminal to watch to watch this content for free
@git-amend
@git-amend 14 күн бұрын
Thanks for the comment, made my day!
@mracipayam
@mracipayam 6 ай бұрын
Who would buy if git amend make a premium course ? Me first.
@git-amend
@git-amend 6 ай бұрын
Thanks for the vote of confidence!
@maciejzareba9563
@maciejzareba9563 2 ай бұрын
Perfection
@git-amend
@git-amend 2 ай бұрын
Thanks!
@toastyshrimp1882
@toastyshrimp1882 6 ай бұрын
not sure if you've mentioned this elsewhere, what is that drawing software you're using to visualize architecture?
@git-amend
@git-amend 6 ай бұрын
That's Excalidraw for Obsidian. You can also use the web version: excalidraw.com/ kzfaq.info/get/bejne/pZaVq65e3ay0Y58.html
@techdave99
@techdave99 6 ай бұрын
He has tried a few in the past. I don't know which one he is using here. Might be listed in the Discord.
@martin.m.kloeckener
@martin.m.kloeckener 6 ай бұрын
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.
@git-amend
@git-amend 6 ай бұрын
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.
@martin.m.kloeckener
@martin.m.kloeckener 6 ай бұрын
@@git-amend Thanks for your answer, that's helpful!
@jeidoz
@jeidoz 3 ай бұрын
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?
@git-amend
@git-amend 3 ай бұрын
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.
@simonnordon8421
@simonnordon8421 6 ай бұрын
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.
@git-amend
@git-amend 6 ай бұрын
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.
@rechnight
@rechnight Күн бұрын
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.
@KhanhLe-in7yi
@KhanhLe-in7yi 2 күн бұрын
Wonderful!! But can I save a dictionary?
@git-amend
@git-amend 2 күн бұрын
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.
@pietro0games
@pietro0games 3 ай бұрын
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?
@git-amend
@git-amend 3 ай бұрын
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.
@atherissquamigera7425
@atherissquamigera7425 6 ай бұрын
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?
@git-amend
@git-amend 6 ай бұрын
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
@atherissquamigera7425
@atherissquamigera7425 6 ай бұрын
Thank you so much
@bane9109
@bane9109 4 ай бұрын
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.
@git-amend
@git-amend 4 ай бұрын
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.
@bane9109
@bane9109 4 ай бұрын
I see, I'll try that out! Thanks for the feedback, I think this is the answer I'm looking for!
@rofu8096
@rofu8096 6 ай бұрын
Can you make a video about Audio System for next week?
@rofu8096
@rofu8096 6 ай бұрын
By the way, this week's topic is very interesting as always. Thank you very much for making videos with such high quality content.
@git-amend
@git-amend 6 ай бұрын
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?
@rofu8096
@rofu8096 6 ай бұрын
@@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.
@cileth
@cileth 6 ай бұрын
@@git-amend Would be neat to see how to get unity setup with fmod or wwise if you've ever used those. From a feature standpoint...general system setup, playing one off sounds, playing looping sounds, spatial audio relative to player. Basic music stuff could be cool too like a system to manage current song, transitioning, looping. I know fmod has some variable connections to control multi channel audio stuff and apply audio effects and filters (like different reverb values depending on indoor/outdoor for example).
@CharlieClark-ns3qr
@CharlieClark-ns3qr 3 ай бұрын
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.
@git-amend
@git-amend 3 ай бұрын
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
@CharlieClark-ns3qr
@CharlieClark-ns3qr 3 ай бұрын
@@git-amend Awesome thank you so much for the reply, I appreciate it.
@Briezar
@Briezar 6 ай бұрын
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?
@Briezar
@Briezar 6 ай бұрын
great video btw! Another immediate watch for me.
@git-amend
@git-amend 6 ай бұрын
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!
@damonfedorick
@damonfedorick 6 ай бұрын
Nice.
@git-amend
@git-amend 6 ай бұрын
Thank you! Cheers!
@berkaykzltug5325
@berkaykzltug5325 2 ай бұрын
ISaveable only lets me init and not set. I am using the drawer from the repository as well. How can I fix it?
@git-amend
@git-amend 2 ай бұрын
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.
@berkaykzltug5325
@berkaykzltug5325 2 ай бұрын
@@git-amend Thanks for the quick reply! I'll look into it. Great video!!
@nanaschi
@nanaschi 5 ай бұрын
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
@git-amend 5 ай бұрын
Are you using the PropertyDrawer from the repository?
@nanaschi
@nanaschi 5 ай бұрын
@@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 :)
@nanaschi
@nanaschi 5 ай бұрын
​@@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
@nanaschi
@nanaschi 5 ай бұрын
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
@bane9109
@bane9109 4 ай бұрын
@@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)
@Tofkaai
@Tofkaai 6 ай бұрын
Is it safe to persist the instance ID of the scriptable objects for items? Are they not liable to change?
@git-amend
@git-amend 6 ай бұрын
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.
@whyv9338
@whyv9338 6 ай бұрын
What's the name of the program used to make the diagrams? It looks really clean and simple.
@git-amend
@git-amend 6 ай бұрын
That's Excalidraw for Obsidian. You can also use the web version: excalidraw.com/ kzfaq.info/get/bejne/pZaVq65e3ay0Y58.html
@whyv9338
@whyv9338 6 ай бұрын
@@git-amend Thank you!
@GarT.
@GarT. 5 ай бұрын
@@git-amendThanks a lot!
@pebandit1
@pebandit1 6 ай бұрын
Would it be possible to have many different GameData instances to save some content in different files ?
@git-amend
@git-amend 6 ай бұрын
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.
@pebandit1
@pebandit1 6 ай бұрын
@@git-amend Thank you !! Great video btw
@ZombieChicken-X
@ZombieChicken-X 5 ай бұрын
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?
@git-amend
@git-amend 5 ай бұрын
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).
@ZombieChicken-X
@ZombieChicken-X 5 ай бұрын
so itd be like Bind ?@@git-amend
@publicmmi
@publicmmi 6 ай бұрын
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
@git-amend 6 ай бұрын
Very astute. As mentioned in the pinned comment and description, you can see an implementation in the repository.
@publicmmi
@publicmmi 6 ай бұрын
@@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.
3 ай бұрын
This might be my only hope for making a save system like Half-Life
@git-amend
@git-amend 3 ай бұрын
Great, I hope it inspires you to build something awesome!
@Nytrock
@Nytrock 4 ай бұрын
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?
@git-amend
@git-amend 4 ай бұрын
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
@jcx5750
@jcx5750 4 ай бұрын
what is that flow diagram you are using sir
@git-amend
@git-amend 4 ай бұрын
That is ExcaliDraw for ObsidianØ kzfaq.info/get/bejne/pZaVq65e3ay0Y58.html
@WelshGuitarDude
@WelshGuitarDude Ай бұрын
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.
@bombmk2590
@bombmk2590 7 күн бұрын
Look up how it works. The odds are for all practical purposes zero.
@OskGame
@OskGame 6 күн бұрын
Can you give more instructions on how to encrypt files to avoid thieves?
@stringbean5920
@stringbean5920 2 ай бұрын
Is it as simple as a few words to save everything, or do you have to identify every little thing to save everything each?
@git-amend
@git-amend 2 ай бұрын
Do mean in as far as determining what to save and binding the data, or performing an actual save of the data?
@stringbean5920
@stringbean5920 2 ай бұрын
A script to save and load everything at once instead of identifying every little thing I want
@bielserrano4325
@bielserrano4325 2 ай бұрын
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!)
@git-amend
@git-amend 2 ай бұрын
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!
@bielserrano4325
@bielserrano4325 2 ай бұрын
@@git-amend Ohh, thank you so much!! I will try to join asap!
How to do MORE with the Observer Pattern
13:09
git-amend
Рет қаралды 10 М.
Building Runtime UI with UI Toolkit In Unity
21:35
Game Dev Guide
Рет қаралды 39 М.
女孩妒忌小丑女? #小丑#shorts
00:34
好人小丑
Рет қаралды 86 МЛН
Parenting hacks and gadgets against mosquitoes 🦟👶
00:21
Let's GLOW!
Рет қаралды 13 МЛН
When you discover a family secret
00:59
im_siowei
Рет қаралды 23 МЛН
Improve Your Unity Code with MVC/MVP Architectural Patterns
15:32
Angular Model - The New Signal-Based 2-way Data Binding
12:52
Decoded Frontend
Рет қаралды 22 М.
How to Implement Blackboard Architecture in Unity C#
28:57
git-amend
Рет қаралды 11 М.
I run untested, viewer-submitted code on my 500-LED christmas tree.
45:17
女孩妒忌小丑女? #小丑#shorts
00:34
好人小丑
Рет қаралды 86 МЛН