Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Access game objects by ID or how to programmatically get a reference to a game object

Discussion in 'Scripting' started by Virtumonde, Oct 21, 2020.

  1. Virtumonde

    Virtumonde

    Joined:
    Apr 15, 2020
    Posts:
    84
    I need to programmatically access my game objects in order to react to changes from a server. Every game object has a server-side ID. I want to use that ID to manipulate game objects. I can access an object using variations of GameObject.Find, but this is inefficient. I think that the way to do it is to have a hash map of IDs as the key and references of game objects as values but I do not know how to get the reference on instantiation. Any ideas?
     
    Last edited: Oct 21, 2020
  2. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    Why not just use Object.GetInstanceID? After that, your hash map seems sensible and straightforward.
     
    Virtumonde likes this.
  3. Virtumonde

    Virtumonde

    Joined:
    Apr 15, 2020
    Posts:
    84
    It seems that there is no way to map an instance ID it to a GameObject reference at runtime. Am I wrong?
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,890
    You are wrong.
    Code (CSharp):
    1. Dictionary<int, GameObject> instanceMappings = new Dictionary<int, GameObject>();
    2.  
    3. GameObject myGameObject;
    4. int serverId = <however you get the object's server ID>;
    5. instanceMappings[serverId] = myGameObject;
     
    Virtumonde likes this.
  5. Virtumonde

    Virtumonde

    Joined:
    Apr 15, 2020
    Posts:
    84
    C# sends variables by value rather than by reference. Would this allow me to GameObject.Destroy() the object or change its properties?
     
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,890
    Where'd you hear that? It's not true. C# has reference types and value types. Reference types are passed by reference. GameObject is a reference type as are all classes. Only a small subset of types are "value types".
    Yes.
     
    Virtumonde likes this.
  7. VishwasGagrani

    VishwasGagrani

    Joined:
    May 12, 2018
    Posts:
    84
    If you don't want to use Find because of it's possible high resource usage. You can drag and drop all the game-objects and use it inside the array of a script attached.
     
    Virtumonde likes this.
  8. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    I think that's pretty much what he proposed in the first place. I believe what he wants to know is, if you have the instance ID and nothing else, can you use that to get the Object with that ID? An "Object.GetObjectFromID(int ID)" function.
     
  9. Virtumonde

    Virtumonde

    Joined:
    Apr 15, 2020
    Posts:
    84
    I think got that from StackOverflow. Thank you!
     
  10. Virtumonde

    Virtumonde

    Joined:
    Apr 15, 2020
    Posts:
    84
    That makes sense it's just that the scene is procedurally generated and everything comes from the server.
     
  11. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,890
  12. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,890
    By the way if you plan on destroying the objects from the Dictionary, make sure you also remove the destroyed objects from the Dictionary.
     
    Virtumonde likes this.
  13. Virtumonde

    Virtumonde

    Joined:
    Apr 15, 2020
    Posts:
    84
    This was very helpful. Thanks, @PraetorBlue.
     
  14. Virtumonde

    Virtumonde

    Joined:
    Apr 15, 2020
    Posts:
    84
    The closest thing I found is EditorUtility.InstanceIDToObject whereas Object.GetObjectFromID(int ID) seems to be non-existent in the API. EditorUtility.InstanceIDToObject also I think can be used only in the editor. Directly referencing at C# level seems like a good idea.
     
    Last edited: Oct 21, 2020
  15. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    That is correct. There is no such function. That is why I put it in scare quotes when I was trying to clarify what I thought you were asking for.
     
  16. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    392
    Code (CSharp):
    1. public static class UnityObjectUtil
    2. {
    3.     [CanBeNull]
    4.     private static readonly Func<int, Object> _findObjectFromInstanceID = null;
    5.     static UnityObjectUtil()
    6.     {
    7.         var methodInfo = typeof(Object)
    8.             .GetMethod("FindObjectFromInstanceID",
    9.             System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
    10.  
    11.         if (methodInfo == null)
    12.             Debug.LogError("FindObjectFromInstanceID was not found in UnityEngine.Object");
    13.         else
    14.             _findObjectFromInstanceID = (Func<int, Object>)Delegate.CreateDelegate(typeof(Func<int, Object>), methodInfo);
    15.     }    
    16.  
    17.     [CanBeNull]
    18.     public static Object FindObjectFromInstanceID(int instanceID)
    19.     {
    20.         if (_findObjectFromInstanceID == null)
    21.             return null;
    22.  
    23.         return _findObjectFromInstanceID(instanceID);
    24.     }
    25.  
    26.     [CanBeNull]
    27.     public static T FindObjectFromInstanceID<T>(int instanceID) where T : class
    28.     {
    29.         if (_findObjectFromInstanceID == null)
    30.             return null;
    31.  
    32.         return _findObjectFromInstanceID(instanceID) as T;
    33.     }
    34. }
    This works in builds, is reasonably performant, and is unlikely to be removed or altered in future Unity versions because it is used by the RaycastHit struct to retrieve colliders. The RaycastHit struct stores references to colliders via an InstanceID - likely to allow more efficient data marshalling from the PhysX integration.

    Keep in mind, these IDs have a limited life cycle. They are very likely to be inconsistent between two different play sessions. The cannot be used to identify objects over networking, or as a means of saving data associated with those objects to be loaded in a future play session. Furthermore, there are only about 4 billion unique IDs available, and they will eventually be recycled.
     
    Last edited: Jan 17, 2023
    CodeRonnie and in0finite like this.
  17. in0finite

    in0finite

    Joined:
    Oct 23, 2017
    Posts:
    17
  18. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,917
    Wow, I haven't noticed they finally made that method accessible from runtime scripts. For the longest time we only had editor only versions of that method. While the InstanceID is more or less an internal concept that Unity uses internally to manage things, since that ID is exposed to the public, it was for the longest time kinda useless. Of course as mentioned in the older posts, you could use your own dictionary to map those IDs to your object, but that requires a lot of extra book-keeping which is kinda unnecessary since Unity itself does have this mapping internally.

    So it's great to see this method finally being available at runtime. Now the last thing missing would be to query an AssetID at runtime.
     
    CodeRonnie and in0finite like this.
  19. in0finite

    in0finite

    Joined:
    Oct 23, 2017
    Posts:
    17
    You absolutely need this method. If you are creating a re-usable library that interacts with Objects based on their id, this is the only way (or Reflection approach mentioned earlier), because your library may not be responsible for creating those Objects at runtime, so you can't actually cache them in a Dictionary.

    However, it seems as if Unity tries to hide this API for some reason. It's hard to find it in documentation. It doesn't link to any other page (eg. to `Object.GetInstanceID()`), nor does any other page link to it ...
     
    PraetorBlue likes this.