Hello, I'm in the phase of learning about data serialisation, since it's the next goal for my project to integrate a Saving/Loading system. Here is my current data structure in a simplified form: CellData - For now it's just the current scene buildindex, so a scene is a cell CellData.ActorDataArray - A state "register" storing all relevant character data for all actors who arrive in this cell, including the player MonoActor - The MonoBehaviour component that every character has MonoActor.ActorData - The relevant character data After trying a lot of stuff, this seems to be the most convenient way to store cells and actors in my case. A bit more info about the game: - The game consists of many scenes/cells, just like Skyrim's world, with an outdoor scene and a lot of trigger connected interior scenes - NPCs are free to move between scenes/cells like the player - Most characters have randomly chosen equipment (clothes, weapons, hairstyles) - All humanoid characters have an inventory What I'm struggling with is to understand how to load a savegame properly. NPCs, Player, all items, all equipped armor, hairstyles, etc. This is how I imagine how it could be done, if a savegame is loaded: 1. Load the scene the player was in when the savegame was created 2. Destroy NPCs who don't belong there 3. Repopulate Player, NPCs from the ActorDataArray from basic character prefabs 4. Instantiate all physical representations of equipment that had the equipped status of "true" 5. In the player inventory UI load all equipment sprites and place them where they belong Does this seem to be ok or is there a better way to handle data in games like Skyrim? May be there is a way that might be better for saving and loading gameobjects and their hierarchies (clothes, weapons, hairstyles)? I would be really grateful for any insight, any help you can offer. How do YOU think this would best be done in Unity?
It sounds like you have a handle on it based on your list of items above. It really comes down to making your game in a way that it is easy to capture the full state, then restore the full state, and as you said it comes in phases: loading the scene, setting stuff up, loading more stuff, etc. And the key point is, every game is going to have subtle edge case requirements. I have randomly blathered about it before too, but it looks like you already have all these points down: https://forum.unity.com/threads/save-system-questions.930366/#post-6087384
Thanks Kurt! This topic really gives me headaches. It also seems that, in a game with a lot of scene loading, singletons and DontDestroyOnLoad objects make things even harder to handle. Singletons loose references, static events have duplicated subscribers. Everything needs to be cleared and re-initialized manually or I'm doing something fundamentally wrong. I have the feeling it's better to create the save system first and then carefully build all other game systems on top of it.
You're not wrong! And you are not alone in this. The trouble is that when you come up with your brand-spankin' new "HEY I GOT A GREAT IDEA!" game idea, the last thing you wanna do is think about load / save. And if you did, you would get discouraged and your great game idea would die in seedling form, completely missing the opportunity of rapid Unity3D development. About all you can do is keep the project and design flexible and refactorable enough that you can rearrange stuff to be compatible with load / save. And in any case, who knows what you even ultimately want to save. Do you need to save every bullethole in an FPS level? I guess you could... but I've rarely seen that done. There's just no value to the player.
Yes, it's really troublesome. Now that I do, there is so much to re-evaluate and all the Unity start functions are a nightmare to handle. I wish there was a way to completely control the exact initialization process manually, by a single function call that calls everything else in a nice, tidy manner. I'm sure there is a way, but to find it I have to do more research.
Load/Save engineering is in no way complicated by Unity. In fact, Unity has extremely clear lifecycles, dictated by the lifetime of scenes and Components on GameObjects. Remember, Unity is just GameObjects with Components, organized into Scenes and Prefabs, with Assets plugged into them. That's it. You get those things set back up properly, the game will continue without a hiccup. Here is some timing diagram help: https://docs.unity3d.com/Manual/ExecutionOrder.html It's just any other kind of engineering, one that could cut across your entire project.
What I found helpful is that Awake(), SceneManager.sceneLoaded and Start() are being called in this order, so at least you have 3 "tiers" of calling functions and setting things up. On a side note - and I hope I am not hijacking this thread - but is it generally a good practice to make the save / load functions (serialization + saving / loading from disc) a coroutine? I am thinking if the hard drive is busy / slow, could it crash the game if this function is not called as coroutine?
I would NOT reach for a coroutine for saving. You are saving a very specific state of the game at a very specific point in time: grab all that data, write it to disk and move on... if something crashes it will NOT be because of it being a coroutine or not. Remember what a coroutine is: it runs on the main thread and code within it is no different than any other code. https://forum.unity.com/threads/des...en-restarting-the-scene.1044247/#post-6758470 https://forum.unity.com/threads/proper-way-to-stop-coroutine.704210/#post-4707821
Thank you very much for your reply - the linked explanations regarding coroutines are also very helpful. They explain the cause of another issue I faced a while ago, when a coroutine wouldn't return properly because I deactivated the script calling it after it's first iteration.
This one is particularly vexing to me: when you set a GameObject to inactive, any running coroutines on all MonoBehaviors on that GameObject actually DIE, they don't just pause execution: they are toast forever. That just seems... wrong. Another weirdness is that disabling the MonoBehavior that the Coroutine is running on does NOT stop coroutines running on it... it doesn't even pause their execution! But once you know these quirks, coroutines can be useful for sure.
I agree. I rather meant the way I set it up without considering serialization kicks my butt now. I sprinkled everything across all possible Awakes and Starts besides custom Init functions and it worked somehow. Now I'm left with a half baked code base that's missing a vital organ. Time for some surgery! The site about "Order of execution" is really helpful! Thanks!