Search Unity

Serialize/deserialize reference to Unity Object

Discussion in 'Scripting' started by llamagod, Sep 20, 2016.

  1. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    I have a custom serialization system and I have a problem, how do I serialize/deserialize a reference to a Unity Object? Unity serializes the fileID and finds the Object itself, there is a hack to get this when working in the editor http://forum.unity3d.com/threads/how-to-get-the-local-identifier-in-file-for-scene-objects.265686/

    But how do I get an Object from the fileID on deserialization?

    I'm currently using the instance ID and, then I get the Object by comparing the instance id with all other Objects in the scene/assets. Is this safe? Can the instance ID change at some point?

    Is there a way for me to serialize and deserialize Objects like Unity does it with something like the JsonUtility class?
     
    EvilTak likes this.
  2. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
    Is this for runtime or editor ?

    If its for runtime, UnitySerializer generates a GUID for each object that it saves and restores references this way, I guess if you want total control over your game data its the safe way.
     
  3. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    Thanks for replying. Serialization is editor (inspector), deserialization is runtime. How do I get this GUID from an Object and how do I get the Object from the GUID?
     
  4. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    Okay, so I can get the GUID with AssetDatabase.AssetPathToGUID, but how do I turn the GUID into a path during runtime. AssetDatabase.GUIDToAssetPath won't work here obviously.
     
  5. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    Basically you can't use GUID's outside of the editor in any way. The only thing you can use is the instanceID and compare with other Objects. Is this safe? I hear people saying instanceID changes sometimes and people who say it never changes, so which one is it? Otherwise if I could just serialize/deserialize with the unity serializer manually that'd be great. As a last resort I can have an asset with a list of Objects and then store my objects there on serialization and let Unity serialize it, then just serialize the indices for the individual Objects and on deserialization grab the objects from the resource.
     
  6. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
    Out of curiosity, why don't you mark your data fields with the [SerializableField] attribute and let unity do its magic in your prefab(s) ? You can even use subclasses with this as well if you mark the class definition with the [System.Serializable] attribute and unity will take care of everything for you...

    ps: UnitySerializer is the name of a formerly famous and great plugin, here's the most recently maintained version: https://gitgud.io/TheSniperFan/unityserializer-ng
     
  7. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    I said it was for a custom serializer already, basically since Unity doesn't support polymorphism I had to write my own workaround where you can submit custom editor data, like a polymorphic list (I've also written a couple of editor scripts for exposing them) and then it serializes it with JSON under what I call a PID (permanent ID) in my project, so the owners can resolve the types themselves, this works flawlessly, it doesn't replace Unity's serialization system, it is more efficient and I don't have to give up on anything. The problem is just that I need to serialize references to Objects, but I don't know how to get Objects on deserialization. Only the instance ID works, but it's unstable, not permanent. fileID and GUID are impossible to convert into Objects on deserialization. So it seems like my only option is the one I explained in the last message (essentially let Unity serialize them in another asset). I certainly am not going to implement thousands of lines of code into my project just to solve a small issue and I don't even know if it would solve it.
     
  8. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
    Ha yeah indeed lack of polymorphism is a pain. I ran into it just a few month ago and someone suggested me to use Protocol Buffer but I ended up just going arround it.

    I'm not sure what your plan is with this but a simple scriptable object should do the trick, have it a store a pair of id/unity object reference in a serialized field then do the linking with your editor scripts. When deserialized instead of polling the unity API you simply ask your serialized object for the reference matching the id. Would that work in your case ?
     
  9. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    That was my plan. Well, except I just need a list of objects and serialize the index, which I'm almost done implementing.
     
    _met44 likes this.
  10. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,336
    fileID to Object would be a really nice feature!
     
  11. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    Yes, absolutely, but since that is handled on the c++ side of the engine and Unity is very strict with letting people into those systems, or just let people manage internal things like fileID I doubt it will happen. Oh and also since I expect their main income is from the millions of people who don't need this advanced stuff at all I think they prioritize fixing general bugs and adding big features over pleasing an obscure elite.
     
  12. IsGreen

    IsGreen

    Joined:
    Jan 17, 2014
    Posts:
    206
    To serialize GameObject reference you can:

    1) Create your own UniqueID: http://forum.unity3d.com/threads/uniqueid.344755/
    2) Use name of GameObject. Of course, you can't repeat any name.
    3) Parent all GameObjects to same Transform, and use child index position to serialize/deserialize GameObject reference.
     
    KamilJu and Shizola like this.
  13. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    Your 1st implementation is a pretty interesting implementation of a unique ID. I don't know how often the UniqueID constructor gets called, though and as it is now when removing the script component the ID doesn't get decreased, so these two factors make it unsafe. If you add a destructor in UniqueID where you decrease it, it would be much better. Also if you're working on a project as a team it's useless if one guy adds a GameObject it increases gets the ID 1 and the pref ID is incremented to 2, then another guy adds a GameObject without having the first guy's changes and it also gets the ID 1, also the ID in the pref file could be different from person to person. That's why I use a shortened 8 characters (2147483648 different ID's) GUID for unique ID's.

    The fact that you have to attach this script to all GameObjects in all scenes makes it not very viable for GameObject ID. Using the name is restrictive, but can be used if absolutely necessary. Third is unsafe because the child index can change.

    All your options are for scene GameObjects, I needed a solution for asset Objects (all derived types). It doesn't matter because I already found the best solution available where I have an asset holding an Object list, then just add objects to that from all over and serialize indices instead of ID's.
     
  14. IsGreen

    IsGreen

    Joined:
    Jan 17, 2014
    Posts:
    206
    For asset you can use path string.

    Unique ID is viable and safe although not continuous numbers. The important thing is not repeated.

    If you work on Project team, you can add autor name variable + UniqueID. Also it is viable and safe.

    ____________

    Third option is safe only when you load/save game: gameObject.transform.GetSiblingIndex(). No for playing.
     
    Last edited: Sep 23, 2016
  15. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    First of all, I'd like to apologize, I said it would be safer if you used a deconstructor. This is false. For example, if you have two ID's one which is 1. the other is 2 and the next ID will be 3, now the first script is removed and the total ID is decreased by one, now when you create a new ID it will have 2 and that means there are duplicates.

    Using the path string means if the path changes your reference to the Object is lost.

    The third option is just stupid using the name would be better here.

    Do not use UniqueID. I tried a similar approach once and it is not safe. First of all, every time you generate a new ID you can never use it again even if you delete the object that has it, this might seem insignificant, but it's bad coding practice. Secondly, you rely on one file for the security of the uniqueness of your objects, adding author name means you now have two variables. As a note using the constructor is bad, because it is called in multiple situations, rather use the OnValidate method and check if the object currently has an ID. Using an 8 digit GUID hex-string is much better because has none of the above vulnerabilities, it always has the same length and it can be converted to a 4-byte array which is the size of an Int32. Even if two GUID's collide (which is virtually impossible) you can just generate a new one in OnValidate. Oh, and did I say 2147483648 different ID's? I meant 2^32 = 4294967296 different ID's since you start at 0 you have half that amount (not that you would ever have that many ID's, but just as a note). My point is just why would you ever prefer your implementation over mine?

    As a note, it is stupid to create a new ID system for GameObjects specifically just for referencing them in a custom editor.
     
  16. EvilTak

    EvilTak

    Joined:
    Feb 4, 2014
    Posts:
    61
    I'm sorry for resurrecting an old post, but @llamagod can you elaborate on your "Object list" approach? In particular, do you add all Objects to the list or only those that are referred to now or will be referred to in the future?