- Home /
Need a persistent unique id for gameobjects
So, let me clarify my exact issue here: I am making a system in which certain GameObjects will get a unique id, that is not only unique, but persists over Unity Editor updates (I'll explain the reason I mention this specifically), game quits/etc.
I already know how to:
Save data to file to persist over game quits/plays.
Create unique IDs with Guids.
For now I have been manually setting new ID's with a custom context menu and method that simply creates a new Guid.
I have tried/combined these two ideas (I did try many others but these two were the closest to being successful):
http://answers.unity3d.com/questions/487121/automatically-assigning-gameobjects-a-unique-and-c.html http://joshuasmyth.maglevstudios.com/post/Generating-Persistent-Unique-Ids-in-Unity3D2 (I event tried copying the code line for line in case I typed something incorrectly and it still would not work...)
This semi worked. It created new IDs for every new gameobject but since unity's build in InstanceID changes everytime the unity editor reloads/scene loads/etc this broke and regenerated the IDs, making persisting IDs impossible.
Is there any simple solution or code that would either make IDs different as soon as this "UniqueID" component is added to a GameObject or a way to keep track of these objects (without a decrease in Unity Editor performance) and recreate IDs if any match/gameobject duplicated?
Thank you for all the help!!
How about placing a "PersistentID" component on each gameobject that needs a persistent ID and storing its ID as a public (i.e. serialized) int?
That way you can assign it a unique id just once, after doing a check to see if any other gameobjects in the scene have the same ID (using FindObjectsOfType), and then you'll have a permanently stored ID that won't change between scene loads.
using UnityEngine;
using System.Collections;
public class PersistentID : $$anonymous$$onoBehaviour {
public int persistentID;
void SetID()
{
persistentID = GetInstanceID();
//etc
}
}
Answer by Eudaimonium · Sep 27, 2016 at 10:43 PM
I flew by this exact problem when I was creating my save/load system for objects.
What I ended up using was actually stupidly simple:
Float which is object's position.SqrMagnitude
Since all saved in-game objects I use are sort of "pick ups" or interactables, they all have their collision boxes and physics. No two objects will occupy exact same space. This is all but guaranteed to be unique for any 3D application.
This is only used for "placable" types, i.e. pickups placed from within the editor. They use IDs (generated at Start, before any physics takes place) to know if they've been picked up, from a list of picked up IDs.
Spawnable or droppable types do not need IDs, since they are spawned from a list of saved objects.
I'll try that out and see if I can get that to work. Sounds pretty simple. Thank you! (will mark this as correct if I get this working)
wouldn't an object at (1, 0, 0) and an object at (0, 1, 0) have the same Sqr$$anonymous$$agnitude ? or say two objects both at (0, 0, 0) ? FlashFrame's suggestion seems more robust.
That is true, they would. It's a quick n dirty solution that works for my own needs so far.
Flashframe's solution appears to only take into account persistance between scene loads, not persistance between game/editor sessions where InstanceIDs change, which I assume is where the guy has a problem because he's trying to do a Save/Load system.
Problem is (what I assume, again), if you place any pickups via Editor, at Start you need to read a list of picked up objects, and if the object is picked up, despawn it before the game starts. (This is inverse from spawning additional objects that have been spawned during gameplay and saved). Hence, the IDs need to be the same every time the game starts up. For that reason I moved away from InstanceIDs.
If you have a component that stores the ID as a public variable, and you only set that ID once, then it'll be persistent between game/editor sessions. You just use the InstanceID to make sure you're generating a unique number. Once it's set, you don't change it. It doesn't matter if the game objects actual InstanceID changes.
I might be missing something, apologies if I am!
So, this is what I decided to do: 1. I actually went with using your idea of the position of the object, but rather than the square magnitude used some other math and combined with letters and a few other random numbers, to guarantee uniqueness. This will be auto generated when the script is added to a GameObject. (I may switch to Guids if this doesn't work completely) 2. Due to the fact that I could not get this to work when duplicating objects (and while I found workarounds, none guaranteed persistence 100% of the time), I just created a custom context menu to reset the IDs if need be.
This is the best solution I could find since it does guarantee persistence and uniqueness if used properly.
Answer by dishmop · Mar 30, 2017 at 02:53 PM
There are a few proposed solutions for this online, but none of them worked for me. The position-based GUID is nice, but breaks if I move one of my game objects.
I came up with another solution, which seems to work for me. It copes with moving your game objects, duplicating them, having prefabs as well as additive scene loading (i.e. the id here is guaranteed to be unique across scenes). It doesn't require any editor scripts and has no run-time overhead when you make a non-editor build.
I've added some comments explaining how it works which might make it look a bit verbose.
// Script for generating a unique but persistent string identifier belonging to this
// component
//
// We construct the identifier from two parts, the scene name and a guid.
//
// The guid is guaranteed to be unique across all components loaded at
// any given time. In practice this means the ID is unique within this scene. We
// then append the name of the scene to it. This ensures that the identifier will be
// unique accross all scenes. (as long as your scene names are unique)
//
// The identifier is serialised ensuring it will remaing the same when the level is
// reloaded
//
// This code copes with copying the game object we are part of, using prefabs and
// additive level loading
//
// Final point - After adding this Component to a prefab, you need to open all the
// scenes that contain instances of this prefab and resave them (to save the newly
// generated identifier). I recommend manually saving it rather than just closing it
// and waiting for Unity to prompt you to save it, as this automatic mechanism
// doesn't always seem to know exactly what needs saving and you end up being re-asked
// incessantly
//
// Written by Diarmid Campbell 2017 - feel free to use and ammend as you like
//
using UnityEngine;
using System.Collections.Generic;
using System;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif
[ExecuteInEditMode]
public class UniqueId : MonoBehaviour {
// global lookup of IDs to Components - we can esnure at edit time that no two
// components which are loaded at the same time have the same ID.
static Dictionary<string, UniqueId> allGuids = new Dictionary<string, UniqueId> ();
public string uniqueId;
// Only compile the code in an editor build
#if UNITY_EDITOR
// Whenever something changes in the editor (note the [ExecuteInEditMode])
void Update(){
// Don't do anything when running the game
if (Application.isPlaying)
return;
// Construct the name of the scene with an underscore to prefix to the Guid
string sceneName = gameObject.scene.name + "_";
// if we are not part of a scene then we are a prefab so do not attempt to set
// the id
if (sceneName == null) return;
// Test if we need to make a new id
bool hasSceneNameAtBeginning = (uniqueId != null &&
uniqueId.Length > sceneName.Length &&
uniqueId.Substring (0, sceneName.Length) == sceneName);
bool anotherComponentAlreadyHasThisID = (uniqueId != null &&
allGuids.ContainsKey (uniqueId) &&
allGuids [uniqueId] != this);
if (!hasSceneNameAtBeginning || anotherComponentAlreadyHasThisID){
uniqueId = sceneName + Guid.NewGuid ();
EditorUtility.SetDirty (this);
EditorSceneManager.MarkSceneDirty (gameObject.scene);
}
// We can be sure that the key is unique - now make sure we have
// it in our list
if (!allGuids.ContainsKey (uniqueId)) {
allGuids.Add(uniqueId, this);
}
}
// When we get destroyed (which happens when unloading a level)
// we must remove ourselves from the global list otherwise the
// entry still hangs around when we reload the same level again
// but now the THIS pointer has changed and end up changing
// our ID
void OnDestroy(){
allGuids.Remove(uniqueId);
}
#endif
}
This is so nice, thanks! But how can I execute this during runtime? I'am creating GameObjects during runtime.
Re: using this at runtime:
This code is not really applicable to runtime objects - it is specifically tackling the problem of objects created in the editor which are persistent across each play through. If you just want a unique ID, you can just use: Guid.NewGuid ().ToString(). But I suspect this doesn't solve the problem you have.
In my current game, I only have one type of runtime object created and I don't need other objects to keep references to them. So I have a Factory class which is responsible for creating and destroying those objects and also keeping a list of them. I then just serialise the Factory. It serialises the number of objects, and then goes through calling "serialise" on each one of them. To deserialise it just does the reverse.
This idea can be extended to cope with a Factory that can create different kinds of objects or objects that need to be referenced by other objects. But if you go down this rabbit hole, you quickly end up building a generalised serialisation system - which can be pretty complex.
Sorry but this failed when I tried to make a prefab from the object and create an instance from the prefab and undo what I have done etc. Basically I was testing how persistent it is and it started to behave weird when I played with instances and prefabs. I think it might be a solution for simple situations but I think it lacks some conditions. You may end up broken links etc. I am going through the same and I found a recent post from Unity in their blogs. His solution checks the conditions extensively to prevent errors (prefabs etc) and it looks more complete:
https://blogs.unity3d.com/2018/07/19/spotlight-$$anonymous$$m-best-practices-guid-based-references/
C# GUID generation will guarantee uniqueness so 99% of this checking code is redundant.
No, it's not. First of all the GUID struct does not ensure or guarantee uniqueness. It's just random enough to be unlikely to generate a GUID that exists already, but it's still possible. At no point does $$anonymous$$icrosoft guarantee the uniqueness.
To quote the docs:
A GUID is a 128-bit integer (16 bytes) that can be used across all computers and networks wherever a unique identifier is required. Such an identifier has a very low probability of being duplicated.
For more information the UUID wikipedia page has some great informations
Though this is not the actual issue here. The problem is to generate a persistent GUID for every object. When you clone an object (Instantiate() at runtime or in the editor) you get a perfect duplicate.
As I linked below, I've created this UUID script which is more robust but in certain edge cases you can still run into issues. It highly depends on where and for what you need a persistent UUID.
Yeah sure and there's a probability that a Boltzmann brain will spontaneously form in front of you right now. The probability of a clash is so astronomically low that GUIDs have been used for UUIDs in massive scale enterprise computing without checking for decades, orders of magnitude below the need to ever worry about it. Even $$anonymous$$icrosoft's own productivity and server software does too. Stop being pedantic I say again, that the duplication checking code is utterly and completely redundant. I never commented on code that is related to persisting it.
Answer by AntonPetrov · Apr 23, 2020 at 02:42 PM
The problem of persistent identifiers solved by Unity here: guid-based-reference
Answer by AtomsInTheVoid · Sep 25, 2020 at 05:52 AM
I feel like everyone's overcomplicating this...
Why not just use the DateTime.Now as it's unique ID?
Works for me, although perhaps not if your blasting out several objects in the exact same ms. For my purposes I will never have that problem.
Lots of reasons.
A few are: Not unique across threads, process, computers etc Often not enough resolution, this repeating ids
It's not about blasting lots out.. it can just be two at a similar time.
In fact, time is already used as a small part of UUID/GUID generation..
There's a reason why UUIDs and GUIDs are in use as an industry standard in the first place.
Answer by Sprintingkiwi · Oct 26, 2017 at 09:14 AM
Maybe this could help:
transform.GetSiblingIndex()