Search Unity

Any Save State tutorial ?

Discussion in 'Scripting' started by Scellow, Nov 8, 2013.

  1. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    Hey,

    Is there any good tutorial to learn save states ? in JS

    I can't find an understandable one for javascript

    Unity should implement an easy way to for save states



    Why can't we just do like in emulators ?
     
  2. XGundam05

    XGundam05

    Joined:
    Mar 29, 2012
    Posts:
    473
    Because Emulators are virtually replicating the hardware, thus the state of everything is known at all times...because the state of the hardware is replicated. It's not a simple or easy problem. Not at all.

    You should figure out what all you need to save for your game and go from there. And you should figure out whether or not you really need to save the state of everything (hint: probably not).
     
  3. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    Oh i thought it was a similar approach for PC games.

    I'm making a solo action rpg ( Zelda-Like ), but i don't know where to start for the save system, what should i store, how should i manage my stuff, i'm a bit lost to be honest, even in documentation i can't find the solution, they just give you the code nothing more .. i don't need the code, i need to know how should i approach this stuff :/
     
  4. BlackMantis

    BlackMantis

    Joined:
    Feb 7, 2010
    Posts:
    1,475
    I would suggest using PlayerPrefs, then if necessary integrate a different system later.

    To use prefs, create a custom function for saving and loading using the Set and Get. As an alt to custom functions you can use Start() and OnApplicationQuit() .
     
    Last edited: Nov 8, 2013
  5. XGundam05

    XGundam05

    Joined:
    Mar 29, 2012
    Posts:
    473
    It's always necessary. On Windows, PlayerPrefs writes to the Registry. Never a good idea for large amounts of data, ever. Serializing to an XML file is simple, fast...and most importantly, never touches the Registry.

    Unity has even said that PlayerPrefs wasn't designed or intended for saving game data...just for storing small amounts of player preference data, such as whether the player prefers a southpaw config for controls or other small amounts of little things...which is independent of save data (and you don't even need PlayerPrefs for that).
     
  6. pauloaguiar

    pauloaguiar

    Joined:
    May 13, 2009
    Posts:
    700
     
  7. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    I already tried PlayerPrefs but it's not really what i need

    I need a way to manage them in a file

    The xml system is, i think, what i need, but i cant understand it .. :/
     
    Last edited: Nov 8, 2013
  8. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    Here is how i imagine my save system

     
  9. BlackMantis

    BlackMantis

    Joined:
    Feb 7, 2010
    Posts:
    1,475
    xml is not as easy as some say because from the examples I've seen you need a few lines just to save one value. Writing to .txt that is fairly simple just a few lines to save and load many lines of data. I've also created a fast and clean system for saving to Sqlite3 db files.

    Your diagrams above are totally doable, and makes since for a basic save system.

    Try to figure out how much data you will need to save and what for. For character data I limit it to 20-Strings, 20-Ints, and 20-Floats. About half the Ints cover bool state with 0 as false and 1 as true. I use ArrayList and a loop to collect all the data into one string, parsing the Ints and floats. Then the one string would be one line on a text doc. Any of the 20 original strings can also be crunched(packed with multiple values) if I need to expand. Then when loaded I split all the values up and back into the Arrays.
     
    Last edited: Nov 8, 2013
  10. pauloaguiar

    pauloaguiar

    Joined:
    May 13, 2009
    Posts:
    700
    Instead of the PlayerPrefs save the register, why not save a simple txt file?
    I think it was better.
    It's a shame.


    The xml, it is confusing to me too.
     
  11. XGundam05

    XGundam05

    Joined:
    Mar 29, 2012
    Posts:
    473
    For XML, you can just create an object that stores the values you need it to in public variables and slap the [Serializable] attribute on the class. You can then just use the built-in .Net Xml Serializer to serialize and deserialize the objects. This article on the matter doesn't seem half bad: XML Serialization and Deserialization Part 1
     
  12. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    Thanks for your help guys, but to be honest i'm really lost!

    I don't know where to start, it's killing me, i'm searching for a solution for days now ..
    Even for the txt solution it's confusing for me

    I need a ultra simple solution to start with
     
    Last edited: Nov 8, 2013
  13. melkior

    melkior

    Joined:
    Jul 20, 2013
    Posts:
    199
    Step 1: write down what needs to be saved

    Examples:
    Player health
    Player location
    Inventory items

    What else is up to you, its your game after all :D
     
  14. roojerry

    roojerry

    Joined:
    Mar 18, 2013
    Posts:
    68
    It really depends on what you game is and how it's laid out. If its level based, you would need to save what level the player has reached. If it's an open world, you would want to save your players position and information about surrounding entities. This isn't really something someone can give you a quick do-all solution for.

    You need to think about what makes up the state of your game, at any point that a user would be able to save, and figure out what information needs to be saved so that you can recreate or reload that state when the user comes back to that save file.
     
  15. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    329
    There are already premade systems for easy saving and loading of your runtime data in the asset store, if you don't want to roll your own.I haven't tried any so it is up to someone else to suggest anything.

    If you want to roll your own:

    First thing to do is forget about the "save system" you just drafted in the image.
    How does your game manage saves? Just saving at predefined points of reentry (like the zelda saga mostly has) or a completely free saving system that restores the exact point of time you saved.

    For the following I am assuming a save anywhere and everything approach is want you want, but it can also be applied to a save point design (Just load the level at the starting point and restore some variables to the player e.g. health, mana, items obtained, quest finished)

    Think about what needs to be saved in your game:
    • You will most likely have save which scene you are currently in. So we need to save Application.loadedLevelName.
    • You will most likely have objects moving around. So the position / orientation of your objects need to be saved. so transform.position, rotation and scale need to be saved
    That is the basic need.
    • You will most certainly have custom scripts attached to your gameobjects which also need their variables to be saved. Either by doing it by hand (writing a save and load function for each of your scripts) or by using the SerializedObject API if you are not using anything too fancy.
    • If you add new objects to the scene using Object.Instantiate() you will need to keep track of them as well.
    • If you are using custom classes that don't reside inside unity (e.g. a static manager class) or are using something "fancy" (e.g. A Dictionary) you have to use some kind of save/load function for it.
    After you have answered what you need you will need to answer to yourself the most important question first. How do I know which object is which? This will be needed if you have objects inside your scene that need to be modified after you reload your save.
    Easiest way would be to identify them by their name, this would only work if they are all unique (three "Enemy(clone)" would be hard to distinguish). Another approach might be some kind of identification script that has a unique id for each (maybe you can use some unity builtin object identification, would have to research that too, but maybe some unity guru might know something off the top of his head).

    With these things answered it is only a matter of chosing how to save your files.
    There are many options.
    • Write your own text file using one line is one object and the values separated by colon: identifier,4,5,6,1,1,1 for name, position(x,y,z) and scale(x,y,z)
    • Some kind of other format: xml, json, yaml, binary files (Using BinaryWriter)
    • These can be made easier with some kind of serializer like the builtin XmlSerializer (Here a little shameless selfpromotion my tutorial for it. (I tried to hide it as much as possible ^^).

    The process for savegames might go like the following
    • Gather all the data (using GameObject.FindObjectsOfType() or any other means to get all objects you want).
    • Write the data to your chosen format (this highly depends on what you choose, though I would recommend using some kind of intermediate structure and use a serializer). e.g.
      Code (csharp):
      1.  
      2. class EnemySaveState
      3. {
      4.    var name : String;
      5.    var position : Vector3;
      6.    var scale : Vector3;
      7. }
      8. //Saving
      9. var enemy : GameObject = //... get your enemy to save
      10. var save : EnemySaveState = new EnemySaveState();
      11. save.name = enemy.name
      12. save.position = enemy.transform.position;
      13. save.scale = enemy.transform.scale;
      14. xml.save(save, "path/to/enemy/file");
      15.  
    • Loading it again after the player chooses so.
      Code (csharp):
      1.  
      2. //Loading if the enemy exists in the scene by default (after loading it)
      3. var save : EnemySaveState = xml.load("path/to/enemy/file");
      4. var enemy : GameObject = GameObject.Find(save.name)
      5. enemy.transform.position = save.position;
      6. enemy.transform.sale = save.scale;
      7.  
      8. //Keep in mind none of this is actual code and will not run if just pasted into a script file.
      9.  
    • As this task can become quite tedious if you have several things to save and load I encourage you to try methods to automate saving after you managed to save something basic like the example above. e.g. Try the mentioned SerializedObject API to automatically discover which properties are present on a Script and use these to write to your file.

    Beware that not every option works on every plattform (using the webplayer you can't just write a file to the users disk)

    If you just wanted to know about how to make find your saves like in the menu, I apologize for the long answer. Just use Directory.EnumerateFiles/Directories and store them in a common directory.

    Last but not least: Think about what happens if the structure of your data or your content changes. e.g. Version 1.1 brings an update that changes some objects position, upon loading the savegame you could end up stuck in a tree. You could also find yourself in need of adding new fields to your savegame e.g. you just introduced mana to your game (or a new item), do old savegames work after such changes? do they need a migration strategy? Or just dump all old savegames and let the user start anew.
     
  16. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    Wow, thanks for all your explanation !!!

    I thought it was a simple task but looks like its a pain to build :(

    I'm still lost, why can't it be like that ??
    Code (csharp):
    1.  
    2. function saveGame()
    3. {
    4. save.state("save1");
    5. }
    6.  
    7.  
    8. function loadGame()
    9. {
    10. load.state("save1");
    11. }
    12.  

    I just want to save my game not selected stuff, i want like a memory dump, something simple

    If i understand your post, a save game is multiple files ?? enemy, players, entities ??

    But why standard games offer only one save game file ?


    Is there an option to automatically save everything in a file ?


    Sorry i probably sounds like a stupid guy, but i don't know why i can't understand this S*** and it's killing me, i'm very furious :(
     
    Last edited: Nov 8, 2013
  17. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    I found a tutorial witch helped me a lot!


    Saving:
    http://www.youtube.com/watch?v=kxNpcB8UMgY

    Loading:
    http://www.youtube.com/watch?v=lIEpsbQtfFU


    But if i understood everything, it would result to a LOOOT of lines to handle everything, such as boss done, chest opened, key found

    This is this part that i don't understand, how should i manage my project to make it easier to do, and to avoid to write a LOOOT of lines ?
     
  18. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    why don't you look into an offerd system from the asset store then (as billykater suggested)? the creators have put some effort into them to make them as easy to use as possible. but i have not tried to use them myself.

    usually you can put all data into one file. its up to you to interpret it correctly.

    a "good approach" in my opinion is not to have everything as gameobjects but have a separate data class which is then interpreted by the gameobjects/prefabs to display the content properly. so you divide data and the view of it. then you can "simply" safe your data and the game restores itself from it.

    i usually use a manual method to write all required data "manually" to a binary stream. this leaves me more control of when what is saved in contrast to automatic serialization.

    for your special problem you could have a "global" savemanager where all objects which need to be safed register themselves (with an interface fe) and when saving is needed the manager calls a function on each registered object. anyway you need some kind of type information to restore the correct objects. all this can get a mess really quick so why not use a premade system? iirc whydoidoit and marrrk offer one (maybe even free).
    and i have seen a video where save / load functionality has been added to the angry bots demo in a couple of minutes. i think this was the package from whydoidoit.
     
    Last edited: Nov 9, 2013
  19. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    Thank you guys for posting here !

    I have a little issue for saving List here is my script :

    ManagerClass :
    Code (csharp):
    1. public class ManagerClass {
    2.  
    3.     public var type: Type;
    4.     public var id: int;
    5.     public var done: boolean;
    6.  
    7.  
    8.  
    9. public enum Type{
    10.     chest,
    11.     heart
    12.  
    13. };

    ItemClass :
    Code (csharp):
    1. public class ItemClass {
    2.  
    3.     public var ItemID : int;
    4.     public var Name : String;
    5.     public var icon : Texture2D;
    6.     public var Description : String;
    7.     public var itemType : ItemType;
    8.     public var ammount : int;
    9.     public var stackAble : boolean;
    10.     public var maxStack : int;
    11.     public var unlocked : boolean;
    12.     public var equipped : boolean;
    13.     public var slot : Slot;
    14.     public var object : GameObject;
    15.  
    16.  
    17. public enum ItemType{
    18.     Weapon,
    19.     Bow,
    20.     Orb,
    21.     Bomb,
    22.     Shield,
    23.     Arrow,
    24.     Item,
    25.     Empty
    26. };
    27.  
    28. public enum Slot{
    29.     L,
    30.     R
    31.  
    32. };
    33.  


    LevelManager_Test.js
    Code (csharp):
    1.  
    2. #pragma strict
    3. import System.IO;
    4. import System.Collections.Generic;
    5. var playerName: String;
    6. var items_test: List.<ManagerClass> = new List.<ManagerClass>();
    7. var saved_inventory: List.<ItemClass> = new List.<ItemClass>();
    8.  
    9. function save() {
    10.  
    11.  
    12.     // save location. Based on player name
    13.     var path : String = "C:/Unity/Save/" + playerName;
    14.    
    15.     // if folder doesn't exist create one
    16.     if (!Directory.Exists(path))
    17.         Directory.CreateDirectory(path);
    18.    
    19.     // save file name    
    20.     var create_text = File.CreateText(path + "/"+"savegame.sav");
    21.  
    22.     create_text.WriteLine("**Player Details**\n");
    23.  
    24.     // write things to save
    25.     create_text.WriteLine("character_name=" + playerName+ "\n");
    26.  
    27.     for ( var i=0; i<items_test.Count;i++)
    28.     create_text.WriteLine("Entities=" + items_test[i] + "\n");
    29.  
    30.     for ( var j=0; j<items_test.Count;j++)
    31.     create_text.WriteLine("inv=" + saved_inventory[j] + "\n");
    32.  
    33.  
    34.     // close
    35.     create_text.Close();
    36.  
    37.  
    38.  
    39. }
    40.  

    Error :
    Code (csharp):
    1.  
    2. ArgumentOutOfRangeException: Argument is out of range.
    3. Parameter name: index
    4. System.Collections.Generic.List`1[ItemClass].get_Item (Int32 index) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Collections.Generic/List.cs:633)
    5. LevelManager_Test.save () (at Assets/Script/LevelManager_Test.js:155)
    6. LevelManager_Test.saveLoadMenu () (at Assets/Script/LevelManager_Test.js:126)

    Any Idea ?
     
    Last edited: Nov 15, 2013
  20. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Copy and Paste got you. :)

    Code (csharp):
    1.  
    2. for ( var i=0; i<items_test.Count;i++)
    3.     create_text.WriteLine("Entities=" + items_test[i] + "\n");
    4.  
    5.  
    6. //!! Looky here, you're checking items_test.Count again instead of saved_Inventory
    7. for ( var j=0; j<items_test.Count;j++)
    8.     create_text.WriteLine("inv=" + saved_inventory[j] + "\n");
    9.  
    10.  
    Corrected code:

    Code (csharp):
    1.  
    2. for ( var i=0; i<items_test.Count;i++)
    3.     create_text.WriteLine("Entities=" + items_test[i] + "\n");
    4.  
    5. for ( var j=0; j<saved_inventory.Count;j++)
    6.     create_text.WriteLine("inv=" + saved_inventory[j] + "\n");
    7.  
    8.  
     
  21. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    Oh thanks a lot !! I should be more careful while i'm using copy/paste ^^'
     
  22. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    Hmm i think i'm doing it wrong :/

    Here is the output of my save file :
    Code (csharp):
    1.  
    2. **Player Details**
    3.  
    4. character_name=test2
    5.  
    6. Entities=ManagerClass // it should be something like that items_test[0](chest,0,false) right ?
    7.  
    8. Entities=ManagerClass
    9.  
    10. Entities=ManagerClass
    11.  
    12. Entities=ManagerClass
    13.  
    14. Entities=ManagerClass
    15.  
    16. Entities=ManagerClass
    17.  
    18. Entities=ManagerClass
    19.  
    20. Entities=ManagerClass
    21.  
    22. Entities=ManagerClass
    23.  
    24. Entities=ManagerClass
    25.  
    26. Entities=ManagerClass
    27.  
    28. inv=ItemClass
    29.  
    30. inv=ItemClass
    31.  
    32. inv=ItemClass
    33.  
    34. inv=ItemClass
    35.  
    36. inv=ItemClass
    37.  
    38. inv=ItemClass
    39.  
    40. inv=ItemClass
    41.  
    42. inv=ItemClass

    Why it doesn't give me the detail ?
    What do you think about what i'm doing guys ?

    I have chests in my scene, and it send a message when they are already opened so i can check wich one is already opened on load

    PS : I tested Unity Serializer from @Whydoidoit and for some reason it doesn't work well on my project , it doesn't remenber destroyed object with the same name ..
     
    Last edited: Nov 15, 2013
  23. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    I'm about to give up, making a game is a difficult task when you are alone ..
     
  24. Scellow

    Scellow

    Joined:
    Jan 25, 2013
    Posts:
    32
    They really should implement a way to save/load things easily
     
  25. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    329
    for the implementing in unity part: Everybody has different requirements so implementing a system that works for everyone is quite hard. I guess thats why there isn't anything implemented directly in unity.

    As for your problem:
    You are using
    Code (csharp):
    1.  
    2. "Entities=" + items_test[i]
    3.  
    To generate what you want to save to disk. By default this calls items_test.ToString() to generate the string it adds to the file. As you haven't overridden this method yourself the default implementation just outputs the classname of your class. So you either have to override your ToString() method. Or implement a specialized method to handle creating the save string. There is no builtin way to get all fields of a class (e.g. Unity's Vector3 has a custom implementation of ToString() that is like "return string.Format("({0}, {1}, {2})", x, y, z);")

    From my rusty knowledge of Unityscript you would have to write something like
    Code (csharp):
    1.  
    2. function ToString() : String
    3. {
    4.     return "(" + ItemId + "," + Name + "," + Description + ")";
    5. }
    6.  
    Into your item class to get the behaviour you want.
    btw: Saving of something like Texture2d is a little more complex than just adding it to a string. You'll have to think about how to reference the texture.

    As all of this can become quite much work you can take a look at the reflection api to automatically generate the string from any object (e.g. Automatically detect which fields are available). Or just use one of the predefined serializers.