• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
3
Question by falconfetus8 · Jul 05, 2013 at 01:14 AM · id

Automatically assigning GameObjects a unique and CONSISTENT ID: any ideas?

I'm trying to make a system that automatically remembers data between two scenes so that the scenes appear to be "persistent"; IE: If I open a chest in one scene, hop over to another scene, and then hop back to the original scene, the chest should still be open.

The current system I have in place is working BEAUTIFULLY, but there's a major catch: in order for it to work, I need to assign each persistent object an ID number, and this number has to be the same each time the scene is loaded up. At the moment, I'm just assigning this ID number by hand in the inspector when I place each individual object. However, I would like to have a system that automatically assigns each object a unique ID for me.

So far, I've tried doing something similar to this:

 public class PersistenceRememberer : MonoBehaviour {
     
     public static int nextID = 0;
     
     private int myID;
     
     //Events
     
     void Awake(){
     
         myID = nextID;
         nextID++;
     }
 
     void OnLevelEnd(){
         //Clear the nextID
         nextID = 0;
     
     }
 
 }

However, this has a major flaw in it: each time a new scene is loaded, the objects end up getting different IDs.

So, I decided to come here for ideas. How can I automatically assign an ID to an object in such a way that it will always be the same, even when the player hops back and forth between scenes?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

8 Replies

· Add your reply
  • Sort: 
avatar image
2
Best Answer

Answer by Kiloblargh · Jul 05, 2013 at 01:48 AM

 DontDestroyOnLoad (theThingThatKeepsTrackOfYourIDs);

The persistent gameObject could keep a List.< List. >, with the index of the outer list corresponding to the level, and the index in the inner list would serve as the ID for every tracked gameObject in that level.

You can then have another List.< List. > that is populated at the same time as the first list; so the index numbers of this match to the index numbers of your gameObjects, and you can store whatever data you need to.

This would let you not only remember what chest was locked the last time you were in the level, but remotely unlock any chest on any level from any other level.

-edit- Sorry, I think I misunderstood your problem. How about this: The first time you load a level, if the persistent list doesn't exist yet- do something like this for each tracked object:

float posHash = (1000*transform.position.x) + transform.position.y + (.001*transform.position.z);

Unless two objects are in the exact same spot- which they really shouldn't be- this number will be unique, regardless of the order the objects' individual scripts call "Awake()." Then you can sort them by posHash and use their index in the sorted list as their permanent ID.

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image falconfetus8 · Jul 05, 2013 at 08:01 PM 0
Share

The posHash thing is a good idea, but there's an inherent problem with it: what if two objects are in different positions, but end up having the same hash by sheer coincidence? Take, for example, an object located at <1000, 1, .001> and an object located at <1*10^-6, .001, 10000>. They're in different spots, but will have the exact same hash.

avatar image Kiloblargh · Jul 05, 2013 at 08:27 PM 1
Share

Then it can throw an error; and you can move one of them slightly? I did think of that happening as a theoretical problem with my method (which is why I added the different scale factors for each axis) , but is it a practical problem for your case?

You could also calculate the distance between each object and an arbitrary point in 3d space. That seems even less likely to coincidentally give the exact same value (down to floating point precision) for two objects.

avatar image falconfetus8 · Jul 06, 2013 at 07:42 PM 0
Share

I ended up using your idea, and I must say it works great. In the event that I do run into a hash collision, I will just nudge one of them by a little bit by hand. Thanks for the help!

avatar image Em3rgency · Jul 06, 2013 at 07:57 PM 0
Share

Please tick $$anonymous$$iloblargh's answer as correct, or at least close this question :)

avatar image falconfetus8 · Jul 08, 2013 at 02:46 PM 0
Share

I've already given him a "thumbs up". How do I mark his answer as correct?

EDIT: Never$$anonymous$$d. Found the button.

avatar image
0

Answer by SgtOkiDoki · Dec 08, 2018 at 06:55 AM

I end up with this =>

 public float GetID(Transform obj)
     {
         float a = 0;
 
 
 
         System.Text.StringBuilder s = new System.Text.StringBuilder();
         s.Append(obj.name);
         Transform parent = obj.parent;
         a += obj.GetSiblingIndex();
         while (parent != null)
         {
             s.Append(parent.name);
             a += parent.GetSiblingIndex();
 
             parent = parent.parent;
         }
 
         string name = s.ToString();
         char[] c = name.ToCharArray();
         foreach (var item in c)
         {
             a += (int)item;
         }
 
         Vector3 pos = obj.transform.position;
         Vector3 rot = obj.transform.eulerAngles;
         Vector3 scale = obj.transform.localScale;
 
         a += pos.x;
         a += pos.y;
         a += pos.z;
 
         a += rot.x;
         a += rot.y;
         a += rot.z;
 
         a += scale.x;
         a += scale.y;
         a += scale.z;
 
         a += obj.childCount;
 
         return a;
     }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by emanmomot · Oct 04, 2017 at 09:34 AM

In the scene file, every gameobject has a unique identifier that is persistent. You can access it like this:

PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); SerializedObject serializedObject = new SerializedObject(unityObject); inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null); SerializedProperty localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile"); //note the misspelling! int localId = localIdProp.intValue;

The only catch is that this only works in the editor, so you have to serialize it when you want to build your game.

Answer found here: https://forum.unity.com/threads/how-to-get-the-local-identifier-in-file-for-scene-objects.265686/

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
3

Answer by dishmop · Mar 30, 2017 at 03:12 PM

I had problems with the solutions presented here (and elsewhere). The problems cropped up when using prefabs (where ids would get copied into the prefab then duplicated in the instantiations), doing additive loading (when ids would not be unique across level loads) or when making minor cosmetic adjustments to scenes (e.g. moving something slightly or adding in some extra aesthetic objects).

This is my solution to the problem that is working fine for me (so far). More details in the comments of the code.

 // 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 we end up changing 
     // our ID unnecessarily
     void OnDestroy(){
         allGuids.Remove(uniqueId);
     }
     #endif
 }

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by PeterMorris · Oct 27, 2016 at 08:02 AM

I'd use a GUID. Random integers aren't guaranteed to be unique

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
  • 1
  • 2
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

27 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Instantiate ID 2 Answers

Photon Problem 0 Answers

how to create ID to script ?? 0 Answers

store contact with last before destruction 3 Answers

Google Play test in-app purchase non-blank order ID. 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges