using System; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; namespace Unity.Tutorials.Core.Editor { /// /// Used to reference different Unity Objects. /// [Serializable] public class SceneObjectReference { [SerializeField] string m_SceneGuid; [SerializeField] string m_GameObjectGuid; [SerializeField] SerializedType m_SerializedComponentType = new SerializedType(null); [SerializeField] int m_ComponentIndex; [SerializeField] Object m_AssetObject; [SerializeField] GameObject m_Prefab; [NonSerialized] bool m_Initialized; [NonSerialized] Object m_ReferencedObject; SerializedProperty m_SceneGuidProperty; SerializedProperty m_GameObjectGuidProperty; //SerializedProperty m_ComponentTypeProperty; SerializedProperty m_SerializedComponentTypeProperty; SerializedProperty m_ComponentIndexProperty; SerializedProperty m_AssetObjectProperty; SerializedProperty m_PrefabProperty; /// /// The referenced Object. /// public Object ReferencedObject { get { if (!m_Initialized) { Init(); } return m_ReferencedObject; } } /// /// The referenced Object as GameObject. /// public GameObject ReferencedObjectAsGameObject => ReferencedObject as GameObject; /// /// The referenced Object as Component. /// public Component ReferencedObjectAsComponent => ReferencedObject as Component; /// /// Is this a GameObject reference. /// public bool IsGameObjectReference => !string.IsNullOrEmpty(m_GameObjectGuid) && m_Prefab == null && m_SerializedComponentType.Type == null && m_AssetObject == null; /// /// Is this a Component reference. /// public bool IsComponentReference => !string.IsNullOrEmpty(m_GameObjectGuid) && m_Prefab == null && m_SerializedComponentType.Type != null && m_AssetObject == null; /// /// Is this an Asset reference. /// public bool IsAssetReference => string.IsNullOrEmpty(m_GameObjectGuid) && m_Prefab == null && m_SerializedComponentType.Type == null && m_AssetObject != null; /// /// Is this a Prefab reference. /// public bool IsPrefabReference => string.IsNullOrEmpty(m_GameObjectGuid) && m_Prefab != null && m_SerializedComponentType.Type == null && m_AssetObject == null; /// /// Is the reference resolved, that is, pointing to a valid object. /// public bool ReferenceResolved => m_AssetObject != null || ReferencedObject != null || string.IsNullOrEmpty(m_GameObjectGuid); /// /// The Scene the referenced GameObject/Component belongs to, if this is a GameObject/Component reference. /// public SceneAsset ReferenceScene => AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(m_SceneGuid)); /// /// Default constructs this class to uninitialized state. /// public SceneObjectReference() { } /// /// Constructs from a SerializedProperty. /// /// public SceneObjectReference(SerializedProperty property) { m_SceneGuidProperty = property.FindPropertyRelative("m_SceneGuid"); m_GameObjectGuidProperty = property.FindPropertyRelative("m_GameObjectGuid"); m_SerializedComponentTypeProperty = property.FindPropertyRelative("m_SerializedComponentType.m_TypeName"); m_ComponentIndexProperty = property.FindPropertyRelative("m_ComponentIndex"); m_AssetObjectProperty = property.FindPropertyRelative("m_AssetObject"); m_PrefabProperty = property.FindPropertyRelative("m_Prefab"); m_SceneGuid = m_SceneGuidProperty.stringValue; m_GameObjectGuid = m_GameObjectGuidProperty.stringValue; m_SerializedComponentType = new SerializedType(Type.GetType(m_SerializedComponentTypeProperty.stringValue)); m_ComponentIndex = m_ComponentIndexProperty.intValue; m_AssetObject = m_AssetObjectProperty.objectReferenceValue; m_Prefab = m_PrefabProperty.objectReferenceValue as GameObject; Init(); } void Init() { m_Initialized = true; m_ReferencedObject = null; EditorSceneManager.sceneOpened += OnSceneOpened; SceneManager.sceneLoaded += OnSceneLoaded; if (m_AssetObject != null) { m_ReferencedObject = m_AssetObject; return; } if (m_Prefab != null) { m_ReferencedObject = m_Prefab; return; } if (string.IsNullOrEmpty(m_GameObjectGuid)) { return; } var guidComponent = SceneObjectGuidManager.Instance.GetComponent(m_GameObjectGuid); if (guidComponent == null) { return; } GameObject go; m_ReferencedObject = go = guidComponent.gameObject; if (m_SerializedComponentType.Type == null) return; var componentType = m_SerializedComponentType.Type; if (componentType == null) return; var components = go.GetComponents(componentType); if (components.Length == 0) { Debug.LogWarning("Component " + componentType + " not found."); ResetReference(); SaveProperties(); return; } if (m_ComponentIndex + 1 > components.Length) { Debug.LogWarning("Component with given index " + m_ComponentIndex + " did not exist"); m_ComponentIndexProperty.intValue = m_ComponentIndex = 0; } m_ReferencedObject = components[m_ComponentIndex]; } void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ResetInitialization(); } void OnSceneOpened(Scene scene, OpenSceneMode mode) { ResetInitialization(); } // TODO unused void OnSceneClosed(Scene scene) { ResetInitialization(); } /// /// Resets/updates this reference to a new object. /// /// public void Update(Object newObject) { ResetInitialization(); ResetReference(); if (newObject == null) { SaveProperties(); return; } SceneObjectGuid guidComponent; GameObject go; if (newObject is Component) { Component component = newObject as Component; go = component.gameObject; guidComponent = go.GetComponent(); m_SerializedComponentType = new SerializedType(component.GetType()); m_ComponentIndex = Array.IndexOf(go.GetComponents(component.GetType()), component); } else if (newObject is GameObject) { go = newObject as GameObject; if (PrefabUtility.IsPartOfPrefabAsset(go)) { m_Prefab = go; SaveProperties(); return; } guidComponent = go.GetComponent(); } else { m_ReferencedObject = m_AssetObject = newObject; SaveProperties(); return; } if (guidComponent == null) { guidComponent = go.AddComponent(); Undo.RegisterCreatedObjectUndo(guidComponent, "Created GUID component"); } m_GameObjectGuid = guidComponent.Id; m_SceneGuid = GetSceneId(go); if (string.IsNullOrEmpty(m_SceneGuid)) { Debug.LogError("The scene needs to be saved"); return; } SaveProperties(); } void ResetReference() { m_SceneGuid = m_GameObjectGuid = null; m_SerializedComponentType = new SerializedType(null); m_ComponentIndex = 0; m_AssetObject = null; m_ReferencedObject = null; m_Prefab = null; } void ResetInitialization() { m_Initialized = false; EditorSceneManager.sceneOpened -= OnSceneOpened; SceneManager.sceneLoaded -= OnSceneLoaded; } void SaveProperties() { if (m_GameObjectGuidProperty == null) { return; } m_GameObjectGuidProperty.stringValue = m_GameObjectGuid; m_SceneGuidProperty.stringValue = m_SceneGuid; m_SerializedComponentTypeProperty.stringValue = m_SerializedComponentType.Type == null ? "" : m_SerializedComponentType.Type.AssemblyQualifiedName; m_ComponentIndexProperty.intValue = m_ComponentIndex; m_AssetObjectProperty.objectReferenceValue = m_AssetObject; m_PrefabProperty.objectReferenceValue = m_Prefab; } string GetSceneId(GameObject gameObject) => AssetDatabase.AssetPathToGUID(gameObject.scene.path); } }