GetInstanceID - What is it really for?

I use Guid as unique identifier for GameObject when store it to disk.
Because “Unity Guid” not fully constant I attached to these GameObjects the script which generate self unique guid, and these guid will store with Gameobject in the Scene.

[ExecuteInEditMode]
public class StoreObjects : MonoBehaviour
{
   [ReadOnly]
   [SerializeField] private string guid;
#if UNITY_EDITOR
   /// <summary>
   /// Create a new unique ID for this object when it's created
   /// </summary>
   private void Awake()
   {
       if (!Application.isPlaying && String.IsNullOrEmpty(guid))
       {
           Debug.Log("StoreObjects : Awake() - Generated NewGiud");
           //Undo.RecordObject(this, "Generated NewGiud");
           guid = Guid.NewGuid().ToString();
           PrefabUtility.RecordPrefabInstancePropertyModifications(this);
       }
   }
#endif
}

P.S> I took for base the article Good article, but code have little issues - it doesn’t store values GUID in case when the GameObject in the scene (instantiated prefab and unpack complete). And Update called very often especial in Play Editor mode. Ultimately I stayed on next variant base on Awake but modificated, It generate newGuid only then attached to Gameobject and use the attribute [ReadOnly] for field guid to exclude the problem of manually delete the value guid, before run game in Editor.
Therefore it will run only once when you start/stop game.

I used it in my Pooler system and event system based on messages on topics. (Publish\subscribe pattern).
Basically when an object is retrevied from the pull I publish an event like “retrieved-from-pool-”+obj.GameInstanceID()
In this way the object is aware that is just retrieved from the pool and can init itself (listening on event called “retrieved-from-pool-”+gameObject.GameInstanceID() ).

So, GameInstanceID can be used if you want create something that is unique (as the topic names in my case).

I know this is an old topic, but I think about it alot. There are tons of uses for this. So much so that reading this thread almost makes me feel like I’m missing something… Please tell me if I am oblivious to something… but anyway…
Here is the most useful situation I am always using GetInstanceId() for:

Let’s say you have one Gameobject with a couple different Triggers (they could be on child Gameobjects). Each trigger might represent a different function. In otherwords, if there is a collision with trigger A, you want to execute Action_A, if there is a collision with trigger B, you want to execute Action_B, and if there is a collision with trigger C, you want to execute action C.

So when OnTriggerEnter occurs, how do you know which trigger these events are coming from? Well there are tons of ways, but they easiest way that I have found is to store references to each trigger as properties in you Monobehaviour. Then just compare the event collider’s instance id to your references. For example:

public Collider ActionATrigger;
public Collider ActionBTrigger;
public Collider ActionCTrigger;
OnTriggerEnter(Collider other){
    if(other.GetInstanceId() == ActionATrigger.GetInstanceId()){
        Action_A();
    }
    else if(other.GetInstanceId() == ActionBTrigger.GetInstanceId()){
        Action_B();
    }
    else if(other.GetInstanceId() == ActionCTrigger.GetInstanceId()){
        Action_C();
    }
}

Are there much easier ways to do this that I am unaware of?

other is already a Collider component on some object.
by doing other.gameObject you get a direct reference to that object.

2 Likes

Also, == with Unity Objects is already comparing instance ids (plus some additional checks if the native object hasn’t been destroyed).

So other == ActionATrigger is already roughly equivalent to other.GetInstanceID() == ActionATrigger.GetInstanceID(). And the first should probably be preferred due to additional checks.

5 Likes

Thanks @orionsyndrome and @

That’s the detail I needed. Thanks!

Hi, sorry for necro posting, but does anybody know if i load object from asset bundle, then unload it, then load again, would it have same InstanceID?

Most likely not. The instance ID is used internally by the engine and it’s not globally unique and will never have collisions within a single session. It could be that they are the same, but it simply something you should not rely on.

2 Likes

Thats not exactly good new for me. I want to assign some external data to assets i load from bundles, but i do not want to always keep them in memory. So i need a way to identify unique asset.

Then generate a GUID, turn it into a string, and serialize that with your data.

1 Like

How can i generate and attach GUID for unity builtin types?

You can’t. Though you can use the Addressables package which is all about defining GUIDs / addresses to assets which can be loaded through all sorts of different means.

Depends on what exactly you want.

So I’ll show you how I deal with the fact that System.Guid is not serializable by default. I wrote a struct that mirrors the shape of System.Guid exactly:

    [System.Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct SerializableGuid
    {

        #region Fields

        [SerializeField]
        public int a;
        [SerializeField]
        public short b;
        [SerializeField]
        public short c;
        [SerializeField]
        public byte d;
        [SerializeField]
        public byte e;
        [SerializeField]
        public byte f;
        [SerializeField]
        public byte g;
        [SerializeField]
        public byte h;
        [SerializeField]
        public byte i;
        [SerializeField]
        public byte j;
        [SerializeField]
        public byte k;

        #endregion

Yes it’s a bit more code that converting it to a string, but I don’t care. It means I don’t have to do any parsing or what not… which in terms of deserializing a prefab or a scene isn’t all that difference in cost. But if say you’re using netcode for gameobjects this actually is much more beneficial when transmitting remotely.

Now I can easily cast to a System.Guid by calling the constructor that accepts these same values:

public System.Guid ToGuid() => new System.Guid(a, b, c, d, e, f, g, h, i, j, k);

But importantly I can cast from a System.Guid to this SerializableGuid by using unsafe C# allowing me to get the address of the guid, cast it to a SerializableGuid pointer, then dereference that pointer:

        public unsafe static implicit operator SerializableGuid(System.Guid guid)
        {
            return *(SerializableGuid*)&guid;
        }

(note - &guid is getting the address of the variable ‘guid’, (SerializableGuid*) is casting it to our pointer, then the leading * on the front is the dereference… you read this logic right to left)

I have other various simple method associated with it and you can find it all in here:
https://github.com/lordofduct/spacepuppy-unity-framework-4.0/blob/master/Framework/com.spacepuppy.core/Runtime/src/SerializableGuid.cs

Now of course we will also need a PropertyDrawer… and this is where it gets a little complicated but also gets to your question of “generating a guid”.

So before I get to the PropertyDrawer I will note in the SerializableGuid I have an attribute you can use called ConfigAttribute:

        public class ConfigAttribute : System.Attribute
        {
            public bool AllowZero;
            /// <summary>
            /// Attempts to make the guid match the guid associated with the asset this is on.
            /// Note this only works if it's on an asset that exists on disk (ScriptableObject, Prefab).
            /// Also it means guids will match across all scripts that are on a prefab with this flagged. (since the prefab only has one guid)
            /// Really... this should be mainly done with ScriptableObjects only, Prefab's are just a
            /// sort of bonus but with caveats.
            ///
            /// New instances created via 'Instantiate' or 'CreateInstance' will not get anything.
            /// This is editor time only for assets on disk!
            /// </summary>
            public bool LinkToAsset;

            /// <summary>
            /// Attempts to make the guid match the targetObjectId & targetPrefabId of the GlobalObjectId from the object
            /// for the upper and lower portions of the guid respectively. If LinkToAsset is true, that takes precedance to this.
            /// </summary>
            public bool LinkToGlobalObjectId;

            /// <summary>
            /// The guid will be displayed as an object reference field showing the asset related to the guid.
            /// Dragging an object onto said field will update its value unless any LinkTo* is true.
            /// </summary>
            public bool ObjectRefField;
        }

This allows me to on the fly decorate my editor for it and have different ways to generate the guid.

    public class zTest01 : MonoBehaviour
    {

        public SerializableGuid PlaneGuid; //yes - I am noticing now that I typed plane instead of plain... no point in changing it now. This is what happens when I quickly write up stuff on the spot

        [SerializableGuid.Config(AllowZero = true)]
        public SerializableGuid AllowZeroGuid;

        [SerializableGuid.Config(LinkToAsset = true)]
        public SerializableGuid LinkToAssetGuid;

        [SerializableGuid.Config(LinkToGlobalObjectId = true)]
        public SerializableGuid LinkToGlobalObjectIdGuid;

        [SerializableGuid.Config(ObjectRefField = true)]
        public SerializableGuid ObjRefGuid;

        [SerializableGuid.Config(ObjectRefField = true, LinkToAsset = true)]
        public SerializableGuid ObjRefLinkedGuid;

    }

9783387--1403226--upload_2024-4-20_20-9-25.png

Note the slight difference in each configuration when drawn.

Plain Guid by default ALWAYS is non-zero and automatically generates a guid for you (random-ish via System.Guid.NewGuid). There is a button the generate a new one.

Allowing a zero-guid defaults it to zero, but still gives you a ‘New Id’ button.

Link to asset guid sets the guid to the guid of the asset this script is attached to (in this case it’s on a prefab named ‘Test’. This guid is what would be found in the Test.prefab.meta file. This is the same guid that Addressables uses for generating its guids for your prefabs). Note because it relies on the asset guid… this option only works if it’s attached to an asset… think Prefab, ScriptableObject, the sort.

LinkToGlobalObjectId links it to… the GlobalObjectId. This can be useful for in scene objects since the asset id of an object in the scene will be the scene’s asset id (that’s that asset in which the script exists), this will be more unique as its the global object id as returned by GlobalObjectId.GetGlobalObjectIdSlow:
https://docs.unity3d.com/ScriptReference/GlobalObjectId.GetGlobalObjectIdSlow.html

Lastly there is the ‘ObjRefGuid’ which sets the guid to the asset guid, but displays it as an object ref field. This one may seem a little weird but I created it before I got into Addressables and it allows me to reference an asset without loading the asset. Then I’d have a factory somewhere that allowed me to load up the asset by said guid (basically what addressables does, but not as feature rich). And it’s convenient to display such things as an object ref field rather than as the guid itself.

But yeah, that’s a lot of different ways to “generate” a guid based on various aspects of your target.

The PropertyDrawer… mind you… it’s a lot. This isn’t trivial stuff that you’re going to grok right off the top of your head. I’m sort of jerking the Unity API around a bit to get this all (also it only works at editor time, this auto generation is all editor based).

Aside from its dependency on my TypeUtil and EditorHelper classes (which are mostly self-explanatory in what they do when you see them) it doesn’t have any major dependencies outside of itself:
https://github.com/lordofduct/spacepuppy-unity-framework-4.0/blob/master/Framework/com.spacepuppy.core/Editor/src/Core/SerializableGuidPropertyDrawer.cs

Honestly, I use this thing a LOT. I love using guids to uniquely identify objects… I don’t know why. And when I first got into Addressables and learned they too tied everything to the asset guid as well it was perfect as it meant my entire system I’d been using up to that point immediately worked with Addressables (as long as it’s in guid mode, addressables is very configurable).

Anyways… figured I’d drop that all here to show how you could definitely have generated guids through the editor if only with a bit of leg work.

2 Likes