Problems creating a random unique string using an editor script

Ok, bit of a strange problem I am having. Basically, I have a method on a script that generates a unique ID for that gameobject. I create an editor script to create a button in the editor, and when pressed creates a string. The chance of any string being the same is mathematically almost impossible.

Here is the script:

  public void GenerateID()
  {
  uniqueID = RandomString(24);
  }
   
  private string RandomString(int size)
  {
  StringBuilder builder = new StringBuilder();
  System.Random random = new System.Random();
  char ch;
  for (int i = 0; i < size; i++)
  {
  ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
  builder.Append(ch);
  }
  return builder.ToString();
  }

Then I use the following Editor script:

  using UnityEngine;
  using UnityEditor;
   
  [CustomEditor(typeof(LightManager))]
  public class LightManagerEditor : Editor {
   
  // Sets a public string that isnt deleted or changed on editor runtime
  public override void OnInspectorGUI()
  {
  EditorGUI.BeginChangeCheck();
  LightManager lightManager = (LightManager)target;
  if(GUILayout.Button("Generate Unique ID"))
  {
  if(EditorGUI.EndChangeCheck())
  {
  Undo.RecordObject(target, "Created Unique GUI");
  lightManager.GenerateID();
  }
  }
  DrawDefaultInspector();
  }
   
  }

OK, so this works absolutely fine. I click the button, I get a random string each time without issue.

However, the problem I am having is that I can have hundreds of these per scene, and remembering to click each one can be a problem, so I wanted an editor script that basically finds all of these in a scene, and then does the job for me on each object.

So, I wrote the following script:

  public void GenerateID()
  {
  LightManager[] lightscripts = level.GetComponentsInChildren<LightManager>();
  for (int i = 0; i < lightscripts.Length; i++)
  {
  lightscripts[i].GenerateID();
  Debug.Log("LightID :" + lightscripts[i].uniqueID);
  }
  }

And here is the associated editor script :

  using UnityEngine;
  using UnityEditor;
  using System.Collections;
  
  [CustomEditor(typeof(LevelManager))]
  public class GenerateID : Editor {
  
  public override void OnInspectorGUI()
  {
  LevelManager levelManager = (LevelManager)(target);
  
  if (GUILayout.Button("Generate ID's"))
  {
  levelManager.GenerateID();
  }
  
  DrawDefaultInspector();
  }
  }

Now, that should actually work… the problem is that it doesnt. It does go through each object and generate an ID, but they end up being two or three that are the same. It seems random, but it seems to generate two or three in a row of the same and ID and apply it to objects.

For example, here is some debug log output

I cannot figure out why this is happening, it should be calling that method to create an ID once per object, but its almost like its getting output and applying it several times, or perhaps its happening too fast for the script to handle?

Anyone have any ideas?

Why not simply Use GetInstanceID()? its a UnityEngine.Object function which means every instance of every asset, gameobject, transform, monobehaviour, and even your LightManager script, either in scene or in project, already has their own unique id.

its how the inspector actually keeps track of exactly which instance each is referred to in a object field

Hi, I’m actually going to use system.guid now as suggested by another. As for the instanceid I’d heard that this changes and isn’t static (that may be wrong) but as I am using this in my save data to setup objects in the scene, it must be static and unique for every object.

Oh, the reason for the same string is that the random function was based on time, and it was executing multiple times at the same time, hence the same strings

My suggestion is to use UnityEngine.Random class instead of System.Random (it has static methods and should not be instantiated).

You are using System.Random wrong. Right now you are instantiating many System.Random at almost the same time (every time RandomString is called, which happens for all of the objects) a lot of these will end up using the same seed (when invoking the System.Random constructor with no arguments a time-dependent seed is used) and therefore provide the same random value.

If you had one System.Random object which was reused across objects when calling RandomString you would not have this problem. So, if you really want to use System.Random instead of UnityEngine.Random you could have your LevelManager instantiate one System.Random and pass it as an argument to GenerateID which will then pass it to RandomString. RandomString will then use that instance instead of creating its own.