178 lines
5.3 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditor.Experimental.SceneManagement;
#endif
// This component gives a GameObject a stable, non-replicatable Globally Unique IDentifier.
// It can be used to reference a specific instance of an object no matter where it is.
// This can also be used for other systems, such as Save/Load game
[ExecuteInEditMode, DisallowMultipleComponent]
public class GuidComponent : MonoBehaviour, ISerializationCallbackReceiver
{
// System guid we use for comparison and generation
System.Guid guid = System.Guid.Empty;
// Unity's serialization system doesn't know about System.Guid, so we convert to a byte array
// Fun fact, we tried using strings at first, but that allocated memory and was twice as slow
[SerializeField]
private byte[] serializedGuid;
public bool IsGuidAssigned()
{
return guid != System.Guid.Empty;
}
// When de-serializing or creating this component, we want to either restore our serialized GUID
// or create a new one.
void CreateGuid()
{
// if our serialized data is invalid, then we are a new object and need a new GUID
if (serializedGuid == null || serializedGuid.Length != 16)
{
#if UNITY_EDITOR
// if in editor, make sure we aren't a prefab of some kind
if (IsAssetOnDisk())
{
return;
}
Undo.RecordObject(this, "Added GUID");
#endif
guid = System.Guid.NewGuid();
serializedGuid = guid.ToByteArray();
#if UNITY_EDITOR
// If we are creating a new GUID for a prefab instance of a prefab, but we have somehow lost our prefab connection
// force a save of the modified prefab instance properties
if (PrefabUtility.IsPartOfNonAssetPrefabInstance(this))
{
PrefabUtility.RecordPrefabInstancePropertyModifications(this);
}
#endif
}
else if (guid == System.Guid.Empty)
{
// otherwise, we should set our system guid to our serialized guid
guid = new System.Guid(serializedGuid);
}
// register with the GUID Manager so that other components can access this
if (guid != System.Guid.Empty)
{
if (!GuidManager.Add(this))
{
// if registration fails, we probably have a duplicate or invalid GUID, get us a new one.
serializedGuid = null;
guid = System.Guid.Empty;
CreateGuid();
}
}
}
#if UNITY_EDITOR
private bool IsEditingInPrefabMode()
{
if (EditorUtility.IsPersistent(this))
{
// if the game object is stored on disk, it is a prefab of some kind, despite not returning true for IsPartOfPrefabAsset =/
return true;
}
else
{
// If the GameObject is not persistent let's determine which stage we are in first because getting Prefab info depends on it
var mainStage = StageUtility.GetMainStageHandle();
var currentStage = StageUtility.GetStageHandle(gameObject);
if (currentStage != mainStage)
{
var prefabStage = PrefabStageUtility.GetPrefabStage(gameObject);
if (prefabStage != null)
{
return true;
}
}
}
return false;
}
private bool IsAssetOnDisk()
{
return PrefabUtility.IsPartOfPrefabAsset(this) || IsEditingInPrefabMode();
}
#endif
// We cannot allow a GUID to be saved into a prefab, and we need to convert to byte[]
public void OnBeforeSerialize()
{
#if UNITY_EDITOR
// This lets us detect if we are a prefab instance or a prefab asset.
// A prefab asset cannot contain a GUID since it would then be duplicated when instanced.
if (IsAssetOnDisk())
{
serializedGuid = null;
guid = System.Guid.Empty;
}
else
#endif
{
if (guid != System.Guid.Empty)
{
serializedGuid = guid.ToByteArray();
}
}
}
// On load, we can go head a restore our system guid for later use
public void OnAfterDeserialize()
{
if (serializedGuid != null && serializedGuid.Length == 16)
{
guid = new System.Guid(serializedGuid);
}
}
void Awake()
{
CreateGuid();
}
void OnValidate()
{
#if UNITY_EDITOR
// similar to on Serialize, but gets called on Copying a Component or Applying a Prefab
// at a time that lets us detect what we are
if (IsAssetOnDisk())
{
serializedGuid = null;
guid = System.Guid.Empty;
}
else
#endif
{
CreateGuid();
}
}
// Never return an invalid GUID
public System.Guid GetGuid()
{
if (guid == System.Guid.Empty && serializedGuid != null && serializedGuid.Length == 16)
{
guid = new System.Guid(serializedGuid);
}
return guid;
}
// let the manager know we are gone, so other objects no longer find this
public void OnDestroy()
{
GuidManager.Remove(guid);
}
}