Uniquely identifying objects

I’m writing an editor script that allows the designer to select certain properties of certain GOs at design time, and then a related script will save those properties to disk, and then be able to read them back in on a separate run.

The problem I’m seeing is that saving to disk and restoring later depends on being able to match a saved chunk of data with the correct run-time object. So I need to know if there’s any way to uniquely identify a GO and/or its components in a manner that transcends both design-time (when the designer selects them for saving) and run-time, without requiring the designer to faithfully supply a unique “name” value for each object to be saved (not a reliable solution).

I see that instanceID is not dependable between runs, much less after editing. But since Unity can match a GO at design-time to a GO at run-time, there’s got to be a way.

design time to runtime matching works through prefabs, not on a per object base.

Prefabs are object blueprints, they don’t have (don’t must) be per object persistent.

If you need per object data with runtime mapping you will have to integrate a stable UID system as well as some kind of database to store and retrieve the data usefully

Aye! Quite the challenge, then. I expect the most challenging will be a persistent UID.

I’m pretty sure this has been done before (I’ve seen a script for sale that does something similar), but I don’t know how they solved the problem.

Oh that part is pretty simple thanks to the fact that you work with .NET: the magic is called reflection (and is how various things in Unity work) → you just reflect the whole object properties to your database and recreate it from that again.

The real effort is implementing that part and structuring the DB to hold any arbitary data.

Well, the trick with the UID is going to be sort of a chicken-and-egg problem. When the editor fires up and the various objects need to retrieve their UID from the database, at the point they’re instantiated they won’t know if they’re new objects that have been dragged onto the scene, or if they’ve just been loaded up by the editor because they’re already part of a scene. So they won’t know whether to generate a new UID for the object and write it to the database, or if they should retrieve their UID from the database. And come to think of it, even if they knew to retrieve it, how would they find it? Without a unique identifier, there’s no way to find the proper record in the database.

Reflection would hold all the answers if the objects were all instantiated at run-time, but the entire goal of this is to allow the designer to visually check which properties they want saved.

Think of it this way: if I create a script like so:

public class MyComponent : Monobehaviour
{
   public GameObject objToMuckWith;
}

Then in the inspector, I can drag another GO in the scene onto what will appear as “Obj To Muck With” and it will save a reference to that GO and be able to access it at run-time. Plus, this relationship is preserved between editor sessions. So there almost has to be a way to piggy-back off of/reproduce this functionality.

I did not try it:

I have the same problem. :slight_smile:

Yeah, my understanding of instanceID, however, is that it can change between sessions. So that means having reproducible and consistent ordering of objects would be out, not to mention being able to tie an instanceID to a stored value between sessions.

It’s true. The instanceID change between sessions. That is bad …

Is there really no unique ID?

Well, this mainly is determined by the level of customizability per object. If it’s just a set of pre-made objects that you’re loading, then it’s a simple matter to assign each one an integer value and then bring them back based on the integer.

What I did for a strategy game prototype I made was this:
• Saving
Part One: Check if the boolean for the slot is false. If so, move on. If not, go through the loading steps, but erase all information from details back out to whether or not the slot occupied.

Part Two: Set the slot’s boolean to true and save the number of objects.

Part Three: Using a while loop, save each object’s type as an integer under the string version of their array position. (“0”, “1”, etc…) Converting from int to string and from string to int isn’t that hard even if you do it by hand.

Part Three: Store any info specific to that object under the array position + name of property. I know which pieces to check for due to the integer ID.

• Loading
Part One: Check if the slot’s boolean is true. If it is, continue. Otherwise, send error message back.

Part Two: Check the integer for the number of items.

Part Three: Check the type for the item and instantiate the prefab for the stored integer.

Part Four: Set that prefab’s settings based on the additional stored information.

This was all done using PlayerPrefs, so it may not be what you’re looking for. However, it was effective enough to store the information for over 100 units with all bullets, damage, current orders, etc… included.

Thanks for the input, GargerathSuman.

Unfortunately for my case, I’m trying to write a highly generalized system that could be dropped into any game and used within seconds, and that requires little to no coding at all to use. I’ve actually come up with a system I think is going to work, but still have to finish implementing it before I know just how well it’ll work. I’ll let you guys know how it goes.

I think, this is a possible solution:

	/// <summary>
	/// Create a globally unique identifier GUID with C#
	/// </summary>	
	public static string GetGuid()
	{
		System.Guid guid = System.Guid.NewGuid();
		return guid.ToString();
	}

Yes, that will certainly get you a guArantees unique ID, but it unfortunately doesn’t solve the real problem of not being able to persist that value between sessions.

If you use that with a editor script, it solves the problem … Save this value in a string field of your class/gameobjects.

Do you mean in a public field? Because that’s how I’m currently doing it. It’s not elegant or “proper” since making it public makes it vulnerable to modification. However, it works.

Yes, it’s not elegant. As private with set/get I get also a Unity error, if I use editor scripts … that should not be… :frowning:

What kind of error are you getting? Can you show me an example of the code that is generating it?

I get 2 error messages if change this to private and use get/set :

first:

nvalidOperationException: Operation is not valid due to the current state of the object
System.Collections.Stack.Peek ()
UnityEngine.GUIClip.Unclip (Vector2 pos)
UnityEngine.GUIUtility.GUIToScreenPoint (Vector2 guiPoint)
UnityEditor.EditorUtility.DisplayCustomMenu (Rect position, System.String[ ] options, System.Boolean[ ] enabled, System.Int32[ ] selected, UnityEditor.SelectMenuItemFunction callback, System.Object userData) [0x00000] (at /Users/build/builds/unity-branches-2.5.x/unity-2.5.x/Editor/Mono/Generated/Utility.cs:237)
UnityEditor.PaneMenu.DropDown (Rect position) (at /Users/build/builds/unity-branches-2.5.x/unity-2.5.x/Editor/Mono/GUI/DockArea.cs:994)
UnityEditor.BaseProjectWindow.HierarchyView () (at /Users/build/builds/unity-branches-2.5.x/unity-2.5.x/Editor/Mono/ProjectWindow/ProjectWindow.cs:647)
UnityEditor.BaseProjectWindow.OnGUI () (at /Users/build/builds/unity-branches-2.5.x/unity-2.5.x/Editor/Mono/ProjectWindow/ProjectWindow.cs:311)
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[ ] parameters, System.Globalization.CultureInfo culture) [0x00000]

secondly:

NullReferenceException: Object reference not set to an instance of an object
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[ ] parameters, System.Globalization.CultureInfo culture) [0x00000]
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[ ] parameters) [0x00000]
UnityEditor.InspectorWindow.OnGUI () (at /Users/build/builds/unity-branches-2.5.x/unity-2.5.x/Editor/Mono/Inspector/InspectorWindow.cs:351)
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[ ] parameters, System.Globalization.CultureInfo culture) [0x00000] at WayPointManager.SetWPGuids () [0x0001c] (at Assets/Scripts/Pathfinding/WayPointManager.cs:197)
WayPointEditor.OnInspectorGUI () (at Assets/Editor/WayPointEditor.cs:68)
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[ ] parameters, System.Globalization.CultureInfo culture) [0x00000]

	/// <summary>
	/// Set a Guid for all waypoints
	/// </summary>
	public static void SetWPGuids ()
	{
		WayPoint[] wp = FindObjectsOfType (typeof(WayPoint)) as WayPoint[];

		for (int i = 0; i < wp.Length; i++) {
			if(wp[i].Guid.Length == 0)
				wp[i].Guid = GetGuid();
		}
	}
public class WayPoint : MonoBehaviour
{
	public int index = 0;
	private string guid;
	/// <summary>
	/// get/set Guid
	/// </summary>
	public string Guid {
		get { return guid; }
		set { guid = value; }
	}

I’m an old C++ guy so I’m still getting to know some of the idiosyncrasies of C#, so I’m wondering if you’re getting an error because, since a string is a reference type, you’re essentially returning a reference to a private member, which is probably a no-no. Try returning a copy of the string instead, like so (untested):

get
{
   string ret;
   guid.CopyTo(0, ret, 0, guid.Length);
   return ret;
}

:smile: Tomorrow more …