Search Unity

  1. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Let us know a bit about your interests, and if you'd like to become more directly involved. Take our survey!
    Dismiss Notice
  4. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  5. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

How to get the Local Identifier In File for scene objects

Discussion in 'Extensions & OnGUI' started by thelackey3326, Aug 31, 2014.

  1. thelackey3326

    thelackey3326

    Joined:
    Sep 17, 2010
    Posts:
    33
    I had run into a situation while working on an editor tool. I needed to reference GameObjects in a scene across editing sessions in a weird way. So, if I have a reference to a scene object, open a new scene, then reopen the original scene, my reference was broken because it's actually a new instance of the object.

    If you put the inspector into debug mode, you can see the Local Identifier In File field under the Instance ID. This integer stays the same across editing sessions. But, as detailed here in a feedback request by D-Lask, there's no function to get that ID for scene objects:
    http://feedback.unity3d.com/suggestions/scene-object-local-file-id-from-instance-id

    UnityEditor.Unsupported.GetLocalIdentifierInFile(int instanceId) always returns zero for scene objects just like D-Lask says.

    After some digging, though, I came up with this.

    Code (CSharp):
    1. PropertyInfo inspectorModeInfo =
    2.     typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
    3.  
    4. SerializedObject serializedObject = new SerializedObject(unityObject);
    5. inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null);
    6.  
    7. SerializedProperty localIdProp =
    8.     serializedObject.FindProperty("m_LocalIdentfierInFile");   //note the misspelling!
    9.  
    10. int localId = localIdProp.intValue;
    Basically, setting a property called inspectorMode on a SerializedObject instance enables two extra serialized properties: m_InstanceID and m_LocalIdentfierInFile. Please note that "identifier" is misspelled.

    New objects have a Local ID value of zero, and get a non-zero value after the scene is saved.
     
    HOEKKII-DEV likes this.
  2. marijnz

    marijnz

    Joined:
    Sep 20, 2012
    Posts:
    46
    Wow man, thanks a lot for sharing your solution, it is exactly what I needed! I needed a way to uniquely identify GameObjects, and this really helps me out. Great job on finding this :)
     
  3. thelackey3326

    thelackey3326

    Joined:
    Sep 17, 2010
    Posts:
    33
    No problem! Hope it helps. One day, I hope Unity makes some nicer way for editor extensions to refer to scene objects across editing sessions. For now, though, this is workable.
     
  4. MarkHenryC

    MarkHenryC

    Joined:
    Nov 30, 2009
    Posts:
    49
    Great work! There should be some sort of Sherlock award waiting for you.

    Thanks for sharing.
     
  5. thelackey3326

    thelackey3326

    Joined:
    Sep 17, 2010
    Posts:
    33
    Happy to share. My favorite thing about Unity is the openness of the community. Game development is traditionally so closed off and secretive, and I don't think that way.

    Now for the shameless plug. I needed the local ID for a tool I just released on the asset store called JumpTo. It's a kind of favorites tool for GameObjects and assets. I'm using the local ID to save scene object links to a file. The next time you load that scene, the JumpTo links load with it. It works okay, but it's quirky.
     
  6. DomDom

    DomDom

    Joined:
    Oct 18, 2013
    Posts:
    32
  7. DomDom

    DomDom

    Joined:
    Oct 18, 2013
    Posts:
    32
    Well I figured out an workaround, basicly just make a copy when being in Unity and use the Serialization to get the copy back from the scene file:

    (don't forget to use [Serializable] on the class)
    Code (CSharp):
    1.  
    2.                 /// <summary>
    3.                 /// This is a copy of the "m_localIndentiferInFile"
    4.                 /// </summary>
    5.                 [SerializeField]
    6.                 private int
    7.                         persistentID = -1;
    8.  
    Then use the code from "thelackey3326" on top to get the "m_LocalIdentfierInFile" [I like the misspelling... I wonder if it will be fixed in Untiy 5? ] , but make sure it has the if for Unity_Editor otherwise you can't create a build!

    Code (CSharp):
    1.  
    2.                 /// <summary>
    3.                 /// Init this instance, it's public so it can be called from a InspectorScript
    4.                 /// </summary>
    5.                 public void init ()
    6.                 {
    7.                         #if UNITY_EDITOR
    8.                         PropertyInfo inspectorModeInfo = typeof(UnityEditor.SerializedObject).GetProperty ("inspectorMode", BindingFlags.NonPublic
    9.                                 | BindingFlags.Instance);
    10.          
    11.                         UnityEditor.SerializedObject serializedObject = new UnityEditor.SerializedObject (comp);
    12.                         inspectorModeInfo.SetValue (serializedObject, UnityEditor.InspectorMode.Debug, null);
    13.          
    14.                         UnityEditor.SerializedProperty localIdProp = serializedObject.FindProperty ("m_LocalIdentfierInFile")  
    15.                         //Debug.Log ("found property: " + localIdProp.intValue);
    16.          
    17.                         persistentID = localIdProp.intValue;
    18.                         #endif
    19.                }
    20.  

    And at last create an InspectorScript which uses OnEnable() to call the init() method.

    Code (CSharp):
    1.  
    2.                 public void OnEnable ()
    3.                 {
    4.                         myPersist.init ();
    5.                 }
    6.  
    This only works once the Scene is saved (before there is no "m_LocalIdentfierInFile" meaning it is == 0) and it also means, you have create a gameobject (or drag'n'drop a prefabe), save the scene and click at least ONCE on the Gameobject! (Which isn't tooo nice... but with this http://answers.unity3d.com/questions/54995/has-anyone-successfullly-used-saveassetsprocessor.html you could proably hook into saving and then call all instances).

    cheers
    DomDom
     
    Last edited: Nov 9, 2014
  8. D-Lask

    D-Lask

    Joined:
    May 18, 2013
    Posts:
    7
    Thanks for posting the workaround! I just thought about looking into a solution again and stumbled upon your post :]
     
  9. DomDom

    DomDom

    Joined:
    Oct 18, 2013
    Posts:
    32
  10. DomDom

    DomDom

    Joined:
    Oct 18, 2013
    Posts:
    32
    Update, make sure to include the line "UnityEditor.EditorUtility.SetDirty (this);" after setting the persistentID (workaround) to prevent overriding from a prefab!
    (see blog post)
     
  11. FlaSh-G

    FlaSh-G

    Joined:
    Apr 21, 2010
    Posts:
    190
    Heya folks,

    I used the info provided in this thread as part of a project of mine. First off, thanks!

    I tested the tool with the Unity 5 beta today and it seems that the m_LocalIdentfierInFile property has the value 0 for objects that do not differ from their prefab/asset counterpart. This is rendering the tool completely useless in Unity 5.

    Can anyone confirm if this is regular Unity 5 behaviour?
    Maybe even if this is intended?

    Greetings
     
    Last edited: Feb 10, 2015
  12. DomDom

    DomDom

    Joined:
    Oct 18, 2013
    Posts:
    32
    I usually had the value 0 when creating a prefab, the scene has to be saved first to get a "m_LocalIdentfierInFile", that's why I meantioned the save hook... I don't know how it is in Unity 5 though...
     
  13. FlaSh-G

    FlaSh-G

    Joined:
    Apr 21, 2010
    Posts:
    190
    I have a scene reload in already, works fine in v4. It does not seem to help in v5 though...
     
  14. thelackey3326

    thelackey3326

    Joined:
    Sep 17, 2010
    Posts:
    33
    I got a Unity 5 beta key so that I could test JumpTo. Well, it doesn't save links to scene instance prefabs or their children for exactly the reason that FlaSh.G describes above. They simply don't have a "local identifier in file." Scene files no longer serialize a prefab into the scene file, so there is no "local" instance to speak of.

    If you add a prefab to your scene, save the scene, then open the scene file in a text editor (of course, it has to be saved in text format), then you can find the prefab by its GUID, which can be found in the prefab's meta file. In the scene file, there will be an entry for your prefab that starts with "Prefab:" and any modifications will be listed underneath.

    So that means that, when a scene is loaded, any prefabs in the scene (that aren't broken links in red) are actually being instantiated from the prefab file and then modified according to the data saved to the scene.

    A coworker of mine had mentioned a few days ago that he had read about this change, and it made me wonder if JumpTo would be broken for Unity 5. Sadly, yes, one of its most attractive features no longer works in 5!
     
  15. FlaSh-G

    FlaSh-G

    Joined:
    Apr 21, 2010
    Posts:
    190
    Dmitriy-Yukhanov likes this.
  16. Dmitriy-Yukhanov

    Dmitriy-Yukhanov

    Joined:
    Jul 27, 2012
    Posts:
    1,038
    This is really silly, we just can't reliably "link" to such prefabs in the scenes in 5+ =(
     
  17. Dmitriy-Yukhanov

    Dmitriy-Yukhanov

    Joined:
    Jul 27, 2012
    Posts:
    1,038
    Looks like we still can get the Local Identifier In File of the scene prefabs in Unity 5 - we need to get the prefab object first using the PrefabUtility.GetPrefabObject().

    It works fine for my purposes (I need to store id to be able to select desired object even if there are few other objects with same path and name nearby).

    Here is an example: https://gist.github.com/DmitriyYukhanov/feb182871a14e355ac38
     
    PedroGV likes this.
  18. FlaSh-G

    FlaSh-G

    Joined:
    Apr 21, 2010
    Posts:
    190
    It's not the same though. I'm less interested in the prefab the object is linked to, but rather the object itself. If multiple GameObjects are linked to the same prefabs, there still seems to be no way to perfectly identify the object. So if the objects have values that differ from the prefab original, we cannot tell which prefab instance that is.
     
  19. Dmitriy-Yukhanov

    Dmitriy-Yukhanov

    Joined:
    Jul 27, 2012
    Posts:
    1,038
    Maybe I'm missing something or just making something stupid, but I just tried my method on such setup:

    prefab file
    scene with 4 instances of prefab
    2 instances differ from prefab file asset (changed some values in components)
    2 instances are totally untouched instances

    And I got IDs of all 4 instances and file asset itself - all 5 values are different: http://i.imgur.com/rBU1dxa.png

    Feel free to let me know where I missed something to let me facepalm on myself =D

    P.S. checked all this on Unity 5.4 beta
    P.P.S. just checked in 5.3 also - same picture.
     
  20. FlaSh-G

    FlaSh-G

    Joined:
    Apr 21, 2010
    Posts:
    190
    Well that's interesting... I will fiddle around with this for a bit and see if this actually works again. Saving a scene in text format yielded for me that it might indeed. Thanks for the info!
     
  21. ofusion

    ofusion

    Joined:
    Dec 5, 2013
    Posts:
    25
    This works. But if the prefab has child gameobject. PrefabUtility.GetPrefabObject() will return the same object for prefab instance and its child gameobject, thus parent and child has same ID!
     
  22. lokinwai

    lokinwai

    Joined:
    Feb 11, 2015
    Posts:
    174
    Using PrefabUtility.GetPrefabParent() can get the local ID same as the prefab in project windows. All same prefab in hierarchy will get the same local ID, but at least child of prefab has different local ID. Use this local ID combine with parent local ID can get the unique ID.

    Code (CSharp):
    1. int localId, localSubId = 0;
    2. PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
    3. SerializedObject serializedObject = new SerializedObject(s_GameObject);
    4. inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null);
    5. SerializedProperty localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile");
    6. localId = localIdProp.intValue;
    7. if (localId <= 0)
    8. {
    9.     PrefabType prefabType = PrefabUtility.GetPrefabType(s_GameObject);
    10.     if (prefabType != PrefabType.None)
    11.     {
    12.         var o = PrefabUtility.GetPrefabObject(s_GameObject);
    13.         if (o == null) o = s_GameObject;
    14.         serializedObject = new SerializedObject(o);
    15.         inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null);
    16.         localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile");
    17.         localId = localIdProp.intValue;
    18.  
    19.         if(s_GameObject != PrefabUtility.FindPrefabRoot(s_GameObject))
    20.         {
    21.             o = PrefabUtility.GetPrefabParent(s_GameObject);
    22.             if (o != null)
    23.             {
    24.                 serializedObject = new SerializedObject(o);
    25.                 inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null);
    26.                 localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile");
    27.                 localSubId = localIdProp.intValue;
    28.             }
    29.         }
    30.     }
    31. }
    32. string uid = localId.ToString() + "," + localSubId.ToString();
     
  23. ofusion

    ofusion

    Joined:
    Dec 5, 2013
    Posts:
    25
    @lokinwai, I get your idea. Combining localId and localSubId, you can get unique string ID
    But I want a unique ID, so if I add localId by localSubId, the result UID can not guarantee to be unique
    Code (CSharp):
    1. int UID = localId + localSubId;
     
  24. lokinwai

    lokinwai

    Joined:
    Feb 11, 2015
    Posts:
    174
    if you want integral id, you may use
    Code (CSharp):
    1. long uid = id * int.MaxValue + sid;
     
  25. elmar1028

    elmar1028

    Joined:
    Nov 21, 2013
    Posts:
    2,031
    Now how would it be possible to find an object by local identifier?
     
  26. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    394
    @Dmitriy-Yukhanov: your method seems to work to get the local Id of the prefab instance, but not for fetching the local Id of each of its components, since it always returns the local Id of that prefab instance.

    Any workaround to get the local Id of the components in a prefab instance?
     
  27. Dmitriy-Yukhanov

    Dmitriy-Yukhanov

    Joined:
    Jul 27, 2012
    Posts:
    1,038
    PedroGV likes this.
  28. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    394
    Done. Thanks!
     
  29. Aldo-V

    Aldo-V

    Joined:
    May 22, 2013
    Posts:
    66
    Thank you so much!
     
  30. tinyant

    tinyant

    Joined:
    Aug 28, 2015
    Posts:
    101
    PedroGV and Dmitriy-Yukhanov like this.
  31. JVLVince

    JVLVince

    Joined:
    Jul 20, 2016
    Posts:
    14
    I know this was an old post but I something I still not understand. I load a prefabs from AssetDatabase then I use the above code to get the m_LocalIdentfierInFile in file Id but something was wrong.
    the Id of transform in my prefabs is 4000012938084824 but when I log it out, it's was 80899090 . As I know it have to be the same??? Is it right? Or I just miss understood something? please point me.
    Thanks in advandce
     
  32. JVLVince

    JVLVince

    Joined:
    Jul 20, 2016
    Posts:
    14
    Arg, I figured it out, I have to get the longValue instead of intValue, since unity 5x have change something (I'm not sure)
     
  33. tinyant

    tinyant

    Joined:
    Aug 28, 2015
    Posts:
    101

    In unity3d 5.6.2 IExposedPropertyTable interface is no-public for us.
    so we can't use ExposedReference<T> is Unity5.6.2.

    Here an example in Unity2017
    https://gist.github.com/frideal/a55961c696acea49d54600c9f66c13ff
     
  34. bourriquet

    bourriquet

    Joined:
    Jul 17, 2012
    Posts:
    35
    Is it the same with Unsupported.GetLocalIdentifierInFile ?
    I work on a prefab, so I shouldn't need the workaround for scene objects, so I use GetLocalIdentifierInFile, but it returns an int, which is not the same as the Debug Mode's "Local Identifier In File". Is there a way to convert it to the right value? Casting to long doesn't help.
     
  35. bourriquet

    bourriquet

    Joined:
    Jul 17, 2012
    Posts:
    35
    Also, this workaround works for reading the fileID. But is there any way to overwrite it? I have tried setting the serializedProperty's longValue, but it doesn't seem to write. I also tried replacing the value directly in the prefab's text file, but then the component gets duplicated and it messes the whole object.
     
  36. JVLVince

    JVLVince

    Joined:
    Jul 20, 2016
    Posts:
    14
    This is how I implement the code, and it's work fine, not only scene objects but also prefabs.
    I don't know how you implement this workaround.
    Code (CSharp):
    1.  public static long GetObjectLocalIdInFile(Object _object) {
    2.         long idInFile = 0;
    3.         SerializedObject serialize = new SerializedObject(_object);
    4.         PropertyInfo inspectorModeInfo =
    5.             typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
    6.         if (inspectorModeInfo != null)
    7.             inspectorModeInfo.SetValue(serialize, InspectorMode.Debug, null);
    8.         SerializedProperty localIdProp = serialize.FindProperty("m_LocalIdentfierInFile");
    9. #if UNITY_5 || UNITY2017_2_OR_NEWER
    10.         idInFile = localIdProp.longValue;
    11. #else
    12.         idInFile = localIdProp.intValue;
    13. #endif
    14.         return idInFile;
    15.     }
    AFAIK, this ID is generated by unity editor, you can't and I don't find out any reason to rewrite it. So my opinion is you shouldn't waste time on this idea :p
     
    Last edited: Apr 20, 2018
  37. bourriquet

    bourriquet

    Joined:
    Jul 17, 2012
    Posts:
    35
    The reason I want to do this is explained here.
    Basically the fileID is changed when I use ReplacePrefab, which breaks references to the prefab's Components, so I want to set the prefab's fileID back to the previous fileID to repair the references.
    In the end I did it by writing directly in the prefab's text content. The inconvenience is that I have to unfocus the Unity Editor in the same execution as I do that, otherwise the references don't come back. And the only way I found to unfocus Unity by code is... EditorApplication.Exit...
    That is so far my only solution to avoid broken references after a ReplacePrefab.
     
  38. naveedkhan358

    naveedkhan358

    Joined:
    Nov 21, 2017
    Posts:
    1
    Tnx for sharing
     
  39. PolishRenegade

    PolishRenegade

    Joined:
    Jul 22, 2010
    Posts:
    592
    I started playing with this as well. It boggles my mind that after 8 years of asking, Unity still hasn't given access to a GUID system for us to use. I used a custom component-based GUID system in the past but it was too heavy to manage and didn't mesh well with other scenes.

    So in the end, we settled for using Local Identifier in File as described here. So far so good. A couple of observations (using Unity 5.6.6);

    1. We use the Transform's local identifier as it will always be different and every GO has one without exception.
    2. At scene save, we change the name of the GO with "[name]![localIdentifier]" ex; BoxRed!284861
    3. Children of prefabs often do not have a local identifier, this is not a problem as the path to the parent can be used and will always be unique (BoxRed!284861/SmallBox) unless you have multiple children name the same, in that case it's workflow question
      1. Quick tool; using a hot-key we copy the proper unique name of any GO, incl children
    4. Weird quirk; In some cases, Unity will add a Local Identifier to prefab's children... all of them at once. As if something was triggered. We still haven't found the reason but we just ignore those and remove the local identifier because it's not robust (it disappears after scene reload or other reasons)
    So far, using multiple scenes in a load-scene-additive workflow we haven't encountered any problems. Scenes have a mix of prefab & non-prefabs, thousands (!) of objects, multi-level hierarchy, static, dynamic, lightmapped, occlusion culling, colliders, lights...