Search Unity

How to save a Transform?

Discussion in 'Scripting' started by DroidifyDevs, Sep 18, 2017.

  1. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
    Hi!

    I'm trying to make a system to save objects on a tile. For example, let's say I have 5 trees spawned on the tile. To save/load them, I'm using JSON, like this:
    Code (CSharp):
    1. [System.Serializable]
    2. public class TileItems
    3. {
    4.     public string InstanceID;
    5.     public List<Transform> ChildObjectTransforms;
    6.     public List<Vector3> ChildObjectPositions;
    7. }
    8. public class TilePopulatorV2 : MonoBehaviour
    9. {
    10.     public TileItems tiles;
    11.     public void SaveData()
    12.     {
    13.         JSONString = JsonUtility.ToJson(tiles);
    14.         File.WriteAllText(Application.persistentDataPath + "/SaveFile.json", JSONString);
    15.         Debug.Log("Saved " + Application.persistentDataPath);
    16.     }
    17.  
    18.     public void LoadData()
    19.     {
    20.         tiles = JsonUtility.FromJson<TileItems>(File.ReadAllText(Application.persistentDataPath + "/SaveFile.json"));
    21.         for (int i = 0; i < tiles.ChildObjectTransforms.Capacity; i++)
    22.         {
    23.             Debug.Log("i is " + i);
    24.             Instantiate(tiles.ChildObjectTransforms[i], tiles.ChildObjectPositions[i], Quaternion.identity, transform);
    25.         }
    26.     }
    27. }
    My problem is that the positions get saved, but the Transforms don't. Therefore, I know where to spawn the trees, but I don't know what tree objects to spawn, and the Instantiate() line in the LoadData() method fails miserably in an ArgumentException.

    How would I go about saving the Transforms, or if it isn't possible, what would be a workaround to achieve my goal?

    So far, the only workaround I've thought of is to spawn empty objects, then manually add the tree model's mesh renderer, rigidbodies, scripts etc... but that seems tedious. It could be done, but I think there's a better way.

    Thanks for any ideas.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    You can't save the Transforms, since they're actually components attached to complex GameObjects.

    Note serializing/deserializing does not result in the same objects, instead it results in all NEW objects. How would it know how to generate the full GameObject for which the Transform is related?

    So... instead what you first need to do is know in what manner the Transforms referenced will exist. For instance... if you're in an already loaded scene, you know they exist, you just need to know which Transforms in said scene you're interested in. Then what you need is a 'token' that relates to those Transforms in scene.

    This could be something like the 'name' of them (if those names are unique), then you could just use the Find method to locate them.

    Otherwise you'll need to generate unique identifiers.

    ....

    If the scene doesn't already exist, and you're trying to rebuild the scene. Wellll.... that's a little tough. You could maybe store a token for the prefab used to construct the GameObject related to that Transform.

    ...

    Anyways, without knowing what these Transforms are supposed to be representing... I can't fully answer your question at this time.
     
  3. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
    Thanks for the detailed reply! I'll add some details.

    The transforms are procedurally spawned preab trees. However, I'm going to add a building system. Let's say a Player cuts down 3 trees on that tile and builds 1 house. Well, I need to save those changes. Therefore, I need to save 1 new house and delete 3 trees from the save file. That's why I needed to save Transforms.

    So the scene does exist, but some elements need to be loaded from save. As for the Transforms, this is what a Tree's inspector looks like (to give you an idea of what I'm trying to save):
    upload_2017-9-18_8-59-47.png

    Perhaps I should simply spawn new prefabs of the trees/houses and then modify their script values if needed.
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    If you're closing the game, and then reloading it again. You'll most definitely have to respawn everything.

    If you're trying to restart the level... maybe not. But then you have 2 different ways of loading the game... which is fine, just means you have 2 styles of code working differently for how to 'load' a scene.
     
  5. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
    I'm thinking that since my world is made up of spawned prefabs, instead of saving a Transform, I can save its name, and then look through a list of all the spawnable prefabs to see which one to spawn. However, this leaves a problem with interactive objects. Let's say a player started cutting a 100HP tree but gave up before saving. So instead of 100HP, the tree has 20HP. Since I'd be spawning a new prefab, the tree would have 100HP again when loading from a save. So I'll need to make a system to save changes to an object's components.

    This has turned into an interesting topic o_O
     
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Ya, my 2 cents on this.. if your tile can have certain "objects" on it, I'd consider just saving the relevant data.
    a gameobject and transform will be created when you load the data in, and if it's 3 trees with HP (100,80,40)
    that's all you'd need. If it's a house, just say that.
    Make your saved data represent the parts you need to know that are different, and don't save something if you don't need it. :)
     
  7. arielfel

    arielfel

    Joined:
    Mar 19, 2018
    Posts:
    33
    Cut - Feb 2019 (~1.5 later)

    Hi guys, if anyone still looking for this implementation.
    (by this I mean how to Serialize Transform for Save&Load Systems in Unity)

    *I made it for my own project... appreciate any Feedback!:)

    upload_2019-2-28_18-18-42.png


    Iv also made static utility class to help its handling when Loading

    upload_2019-2-28_18-34-36.png





     
    KReguiegReply likes this.
  8. Yuira95

    Yuira95

    Joined:
    Jan 17, 2018
    Posts:
    5
    Hi! Can you share your code more largely or give link on project?
    In my project i've trying save transform data player equipments between scenes. For example: On scene 1 i select a hero, after that the scene 2 was starting. On second scene i pick weapons and hats from inventory (weapon/hats prefabs instantiated to pivots on characters after click on item icons). Then i need click apply button to save equipped items and this items should be saved after change scene back to first.
    Probably I should use do not destroy, but where is the guarantee that the objects will keep their position in the pivot? I think i should save worldpos transform data own items and after changing scene back use instantiate with loaded transform data.

    P.S: By the way, you do not know how to say to Unity which hero was chosen from 1 scene? Maybe enumerate them and somehow everything is connected? hmm ...
     
  9. arielfel

    arielfel

    Joined:
    Mar 19, 2018
    Posts:
    33
    Hi, your design needs some (not super complex) DB(Data Base), to answer your question. there is a lot of stuff you can in order to transfer data around your app.
    for start, I recommend understanding what are the vars that live only in real-time and don't need to be saved. then the things need to be saved and understand how to serialize them, from my POV the target of this mechanism is to have as little as stuff needed to be saved in order to load everything that you need, that because handling this code have their own world of wisdom (that is beautiful) but if want to be pragmatic you should use all of Unity feathers like Prefabs and Scriptable objects... anyhow, attaching the code you wish, it will help to save transform values of an object. the function of it is to simply break the transform values to simple and serializable types... I also added extensions method to extend this.transform methods for easy work.
    cheers.


    Code (CSharp):
    1.  
    2. [Serializable]
    3. public class AddonData
    4. {
    5.     public int addonID = 0; //this is drivven from stati variable
    6.     public string addonName = "default addon name";
    7.     public SerializedTransform addonSerializedTransform;
    8.  
    9.     public string textString; // incase of a Text Addon
    10.    
    11.     public void PrintAddonData() // Debug purposes
    12.     {
    13.         //Debug.Log("Addon ID: " + addonID.ToString());
    14.         Debug.Log("Addon Name: " + addonName);
    15.         //Debug.Log("Addon Serialized Transform: " + addonTransform);
    16.     }
    17.  
    18. }
    19.  
    20. [Serializable]
    21. public class SerializedTransform
    22. {
    23.     public float[] _position = new float[3];
    24.     public float[] _rotation = new float[4];
    25.     public float[] _scale = new float[3];
    26.  
    27.  
    28.     public SerializedTransform(Transform transform, bool worldSpace = false)
    29.     {
    30.         _position[0] = transform.localPosition.x;
    31.         _position[1] = transform.localPosition.y;
    32.         _position[2] = transform.localPosition.z;
    33.  
    34.         _rotation[0] = transform.localRotation.w;
    35.         _rotation[1] = transform.localRotation.x;
    36.         _rotation[2] = transform.localRotation.y;
    37.         _rotation[3] = transform.localRotation.z;
    38.  
    39.         _scale[0] = transform.localScale.x;
    40.         _scale[1] = transform.localScale.y;
    41.         _scale[2] = transform.localScale.z;
    42.  
    43.     }
    44. }
    45.  
    46. //Utilities
    47.  
    48. #region LoadingAndSerializationUtilities
    49.  
    50. public static class SerializedTransformExtention
    51. {
    52.     public static void DeserialTransform(this SerializedTransform _serializedTransform, Transform _transform)
    53.     {
    54.         _transform.localPosition = new Vector3(_serializedTransform._position[0], _serializedTransform._position[1], _serializedTransform._position[2]);
    55.         _transform.localRotation = new Quaternion(_serializedTransform._rotation[1], _serializedTransform._rotation[2], _serializedTransform._rotation[3], _serializedTransform._rotation[0]);
    56.         _transform.localScale = new Vector3(_serializedTransform._scale[0], _serializedTransform._scale[1], _serializedTransform._scale[2]);
    57.     }
    58. }
    59.  
    60. public static class TransformExtention
    61. {
    62.     public static void SetTransformEX(this Transform original, Transform copy)
    63.     {
    64.         original.position = copy.position;
    65.         original.rotation = copy.rotation;
    66.         original.localScale = copy.localScale;
    67.     }
    68. }
    69.  
    70.  
     
  10. masterqwertyxxx

    masterqwertyxxx

    Joined:
    Apr 8, 2021
    Posts:
    1
    Code (CSharp):
    1.  
    2. [Serializable]
    3. public class Tile
    4. {...
    5.     public v2 v2MapPos;
    6. [NonSerialized]
    7.     public Vector2 MapPos;
    8. [NonSerialized]
    9.     public Vector3 Rotation;
    10. ...
    11. [Serializable]
    12.     public struct v2
    13.     {
    14.         public float x;
    15.         public float y;
    16.     }
    17. [Serializable]
    18.     public struct v3
    19.     {
    20.         public float x,y,z;
    21.     }
    22. [Serializable]
    23.     public struct v4int
    24.     {
    25.         public int w, x, y, z;
    26.     }
    27. ...
    28.     public void tuData()
    29.     {
    30.         v2MapPos.x = MapPos.x;
    31.         v2MapPos.y = MapPos.y;
    32.     }
    33.     public void suData()
    34.     {
    35.         MapPos.x = v2MapPos.x;
    36.         MapPos.y = v2MapPos.y;
    37. }
    38.  
    39.  
    40.  
    ...