- Home /
Is there a way to tell when a prefab is duplicated?
I have created a component which generates a GUID which allows me to know what prefab a GameObject instance came from, but when a prefab is duplicated the new prefab simply use the same id as the previous one (which is to be expected). So I would like to know if there is a way to detect when prefabs and objects in general are duplicated so that I can generate a new id.
public class GUID : MonoBehaviour
{
public string idString = System.Guid.NewGuid().ToString("N");
private System.Guid _id;
public System.Guid ID
{
get
{
if (_id == System.Guid.Empty && !string.IsNullOrEmpty(idString))
{
_id = new System.Guid(idString);
}
return _id;
}
}
}
[CustomEditor(typeof(GUID))]
public class GUIDInspector : Editor
{
public override void OnInspectorGUI()
{
GUID guid = target as GUID;
GUILayout.Label("ID: " + guid.ID.ToString("N"));
GUILayout.Label("ST: " + guid.idString);
}
}
Some more info on why I need this. This will be part of a system where I need to keep a player inventory/bag and know what prefab to spawn if he drops something form the bag. When a player pick up an item I simply save the GUID to the list of items in the bag while I remove the original GameObject from the world. I have a GameObject in the scene which carry a reference to all the item prefabs so that I can simply use that to find the prefab I want to instantiate. Later I need to expand on this idea to also work for a game save/load system and will have to generate unique IDs for objects in the scene too.
Answer by Univerb Gaming · Apr 26, 2013 at 09:35 AM
Every prefab copied somewhere is bound to either have a different name, time stamp or time stamped meta file produced by unity.
Is it not possible to combine your GUID along with an md5 hash of prefab name + time stamp information extracted from the prefab, meta file or perhaps both and use this as your new identifier?
Thinking about this, filename and path MD5'd should work too.
timestamp is not very reliable especially when transferred via a versioning system or simply copied to another PC. The filename (including the assetpath) would work, but it will glue the asset to this path. Also the main problem was when do you change this information? If it's stored in a string on the prefab it will be cloned when the prefab is duplicated. At runtime you don't have the asset information, so it need to be set at edit time.
I've used this idea to do something like this. The "objectId" is something I'm still playing around with and will be needed for a save/load system later. This code seems to be working pretty well for the rpefabs and the id will only change if the prefab is renamed.
public class GUID : $$anonymous$$onoBehaviour
{
public string prefabId;
public string objectId;
}
[CustomEditor(typeof(GUID))]
public class GUIDInspector : Editor
{
public void OnEnable()
{
CheckPrefabId(target);
CheckObjectId(target);
}
public static void CheckPrefabId(Object target)
{
// apply only to prefabs and prefab intance
PrefabType t = PrefabUtility.GetPrefabType(target);
if (t == PrefabType.Prefab || t == PrefabType.PrefabInstance || t == PrefabType.$$anonymous$$odelPrefab || t == PrefabType.$$anonymous$$odelPrefabInstance)
{
GUID guid = target as GUID;
Object parent = PrefabUtility.GetPrefabParent(target);
string id = AssetDatabase.GetAssetPath(parent ? parent : target);
id = UniRPGUtil.Get$$anonymous$$d5Hash(id);
if (string.IsNullOrEmpty(guid.prefabId) || !id.Equals(guid.prefabId))
{
guid.prefabId = id;
EditorUtility.SetDirty(target);
}
}
}
public static void CheckObjectId(Object target)
{
// do not apply to prefabs
PrefabType t = PrefabUtility.GetPrefabType(target);
if (t == PrefabType.None || t == PrefabType.PrefabInstance || t == PrefabType.$$anonymous$$odelPrefabInstance)
{
GUID guid = target as GUID;
string id = guid.name + guid.transform.position.ToString("F4");
id = UniRPGUtil.Get$$anonymous$$d5Hash(id);
if (string.IsNullOrEmpty(guid.objectId) || !id.Equals(guid.objectId))
{
guid.objectId = id;
EditorUtility.SetDirty(target);
}
}
}
public override void OnInspectorGUI()
{
GUID guid = target as GUID;
GUILayout.Label("Prefab ID: " + guid.prefabId);
GUILayout.Label("Object ID: " + guid.objectId);
}
}
For the thing I'm doing I'm thinking I can have some global setup thing running which does a final sanity check on the prefab and object Ids to make sure they are set and correctly set. It will be a button on an editor window which is part of the bigger picture concerning this whole system.
Answer by Bunny83 · Apr 26, 2013 at 08:15 AM
If i got your right you just need this at edit time to give each prefab a unique ID. In this case you can try using the "quite new" AssetModificationProcessor.
This works for me:
//C#
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class DetectDuplicates : AssetModificationProcessor
{
public static List<string> newAssets = new List<string>();
static void OnWillCreateAsset(string aMetaAssetPath)
{
string assetPath = aMetaAssetPath.Substring(0,aMetaAssetPath.Length-5);
newAssets.Add(assetPath);
}
}
class DetectDuplicatesPostprocessor : AssetPostprocessor
{
static void OnPostprocessAllAssets(string[] importedAssets,string[] deletedAssets,string[] movedAssets,string[] movedFromAssetPaths)
{
if (DetectDuplicates.newAssets.Count == 0)
return;
foreach (var str in importedAssets)
{
if (DetectDuplicates.newAssets.Contains(str))
{
GameObject obj = AssetDatabase.LoadAssetAtPath(str,typeof(GameObject)) as GameObject;
if (obj == null)
continue;
var comp = obj.GetComponent<YourPrefabComponent>();
if (comp == null)
continue;
comp.GenerateNewPrefabID();
}
}
DetectDuplicates.newAssets.Clear();
}
}
In my sample the component attached to the prefab is named "YourPrefabComponent" and it has a public function called GenerateNewPrefabID. This Postprocessor will execute this function if the asset it newly created.
How it works:
The AssetModificationProcessor gets informed when Unity will create a new meta file (for a new asset). I just strip the .meta and save the filename as "new asset" in a List.
When the asset is finally created and imported the AssetPostprocessor will take care of "postprocessing" the actual asset if it's in the list.
I needa study this and see if I can't come up with a cleaner solution using this and what I've done so far.
O$$anonymous$$G, you are a lifesaver. I spent so long trying to figure this out. Is everything from this guaranteed to be a prefab, or do I still need to do a check with PrefabStageUtility to check if the stage is null in 2018.3?
Your answer
