'Unique unchanged identifier for gameobject in unity
I have to store information about every object (gameobject/mesh) in an excel file. Like I have a sphere object I am getting its instanceId and saving it in excel. Within the excel file along with object instance id i am saving additional things like object display name, type.
objInstanceId = transform.GetInstanceID();
My question is that is the safest way? I checked that instanceID is unique but I am not sure if it is gets changed ever? Like if I replace my model will it get changed? I need a unique and unchanged identifier for every single mesh so that I can save it in an excel file and associate some data with the identifier!
Solution 1:[1]
Let's take a look at the docs:
The instance ID of an object is always unique. The ID changes between player runtime and Editor sessions. As such, the ID is not reliable for performing actions between the Editor and runtime sessions, for example, loading an object state from a save file.
https://docs.unity3d.com/2021.1/Documentation/ScriptReference/Object.GetInstanceID.html
So within a runtime session the id is guaranteed to be unique. Between sessions the ids may change.
In other words, exporting all instance ids, adding/changing values and then importing them again will work within a runtime session (when the app is running).
It won't work when you close the app and start another runtime session, in which case any created objects will have different ids.
Solution 2:[2]
If you want a unique persistent ID I would use a serialized field and make it hidden like e.g.
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
// See https://docs.unity3d.com/ScriptReference/ExecuteInEditMode.html
[ExecuteAlways]
public class UniqueID : MonoBehaviour
{
// Serialize/saves the unique ID
// but hide it in the Inspector -> we don't want to edit it manually
// uint allows around 4 Million IDs that should be enough for most cases ;)
[HideInInspector] private uint _id;
private static List<uint> usedIds;
// Public read-only accessor
public uint ID => _id;
#if UNITY_EDITOR
// Due to ExecuteAllways this is called once the component is created
private void Awake()
{
if (!Application.isPlaying && _id == 0)
{
_id = GetFreeID();
usedIDs.Add(_id);
EditorUtility.SetDirty(this);
}
}
#endif
// For runtime e.g. when spawning prefabs
// in that case Start is delayed so you can right after instantiating also assign a specific ID
private void Start()
{
if (Application.isPlaying && _id == 0) _id = GetFreeID();
}
// Allows to set a specific ID e.g. when instantiating on runtime
public void SetSpecificID(uint id)
{
if (Application.isPlaying) _id = id;
else Debug.LogWarning("Only use in play mode!", this);
}
// Stores all already used IDs
private readonly static HashSet<uint> usedIDs = new HashSet<uint>();
private static readonly System.Random random = new System.Random();
// Get a random uint
private static uint RandomUInt()
{
uint thirtyBits = (uint)random.Next(1 << 30);
uint twoBits = (uint)random.Next(1 << 2);
return (thirtyBits << 2) | twoBits;
}
// This is called ONCE when the project is opened in the editor and ONCE when the app is started
// See https://docs.unity3d.com/ScriptReference/InitializeOnLoadMethodAttribute.html
[InitializeOnLoadMethod]
[RuntimeInitializeOnLoadMethod]
private static void InitUsedIDs()
{
// Find all instances of this class in the scene
var instances = FindObjectsOfType<UniqueID>(true);
foreach (var instance in instances.Where(i => i._id != 0))
{
usedIds.Add(instance._id);
}
foreach (var instance in instances.Where(i => i._id == 0))
{
instance._id = GetFreeID();
#if UNITY_EDITOR
// See https://docs.unity3d.com/ScriptReference/EditorUtility.SetDirty.html
EditorUtility.SetDirty(instance);
#endif
usedIDs.Add(instance._id);
}
}
private static uint GetFreeID()
{
uint id = 0;
do
{
id = RandomUInt();
}
while (id == 0 || usedIDs.Contains(id));
return id;
}
}
Note: Typed on smartphone but I hope the idea gets clear.
There might be some edge cases but I hope that gets you started.
As an alternative you could probably also simply use two SerializedDictionary and directly reference forth and back between any type (like GameObject or Mesh) and a unique key (like e.g. the uint).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | sommmen |
| Solution 2 |
