| using System; |
| using System.Collections.Generic; |
| using System.Reflection; |
| using UnityEngine; |
| using UnityEngine.Timeline; |
| using UnityEditor; |
| using UnityEditor.SceneManagement; |
| using UnityEngine.Playables; |
| using Object = UnityEngine.Object; |
|
|
| namespace UnityEditor.Timeline |
| { |
| |
| struct ObjectReferenceField |
| { |
| public string propertyPath; |
| public bool isSceneReference; |
| public System.Type type; |
|
|
| private readonly static ObjectReferenceField[] None = new ObjectReferenceField[0]; |
| private readonly static Dictionary<System.Type, ObjectReferenceField[]> s_Cache = new Dictionary<System.Type, ObjectReferenceField[]>(); |
|
|
| public static ObjectReferenceField[] FindObjectReferences(System.Type type) |
| { |
| if (type == null) |
| return None; |
|
|
| if (type.IsAbstract || type.IsInterface) |
| return None; |
|
|
| if (!typeof(ScriptableObject).IsAssignableFrom(type)) |
| return None; |
|
|
| ObjectReferenceField[] result = null; |
| if (s_Cache.TryGetValue(type, out result)) |
| return result; |
|
|
| result = SearchForFields(type); |
| s_Cache[type] = result; |
| return result; |
| } |
|
|
| public static ObjectReferenceField[] FindObjectReferences<T>() where T : ScriptableObject, new() |
| { |
| return FindObjectReferences(typeof(T)); |
| } |
|
|
| private static ObjectReferenceField[] SearchForFields(System.Type t) |
| { |
| Object instance = ScriptableObject.CreateInstance(t); |
| var list = new List<ObjectReferenceField>(); |
|
|
| var serializableObject = new SerializedObject(instance); |
| var prop = serializableObject.GetIterator(); |
| bool enterChildren = true; |
| while (prop.NextVisible(enterChildren)) |
| { |
| enterChildren = true; |
| var ppath = prop.propertyPath; |
| if (ppath == "m_Script") |
| { |
| enterChildren = false; |
| } |
| else if (prop.propertyType == SerializedPropertyType.ObjectReference || prop.propertyType == SerializedPropertyType.ExposedReference) |
| { |
| enterChildren = false; |
| var exposedType = GetTypeFromPath(t, prop.propertyPath); |
| if (exposedType != null && typeof(Object).IsAssignableFrom(exposedType)) |
| { |
| bool isSceneRef = prop.propertyType == SerializedPropertyType.ExposedReference; |
| list.Add( |
| new ObjectReferenceField() { propertyPath = prop.propertyPath, isSceneReference = isSceneRef, type = exposedType } |
| ); |
| } |
| } |
| } |
|
|
| Object.DestroyImmediate(instance); |
| if (list.Count == 0) |
| return None; |
| return list.ToArray(); |
| } |
|
|
| private static System.Type GetTypeFromPath(System.Type baseType, string path) |
| { |
| if (string.IsNullOrEmpty(path)) |
| return null; |
|
|
| System.Type parentType = baseType; |
| FieldInfo field = null; |
| var pathTo = path.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); |
| var flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | |
| BindingFlags.Instance; |
| foreach (string s in pathTo) |
| { |
| field = parentType.GetField(s, flags); |
| while (field == null) |
| { |
| if (parentType.BaseType == null) |
| return null; |
| parentType = parentType.BaseType; |
| field = parentType.GetField(s, flags); |
| } |
|
|
| parentType = field.FieldType; |
| } |
|
|
| |
| if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(ExposedReference<Object>).GetGenericTypeDefinition()) |
| { |
| return field.FieldType.GetGenericArguments()[0]; |
| } |
|
|
| return field.FieldType; |
| } |
|
|
| public Object Find(ScriptableObject sourceObject, Object context = null) |
| { |
| if (sourceObject == null) |
| return null; |
|
|
| SerializedObject obj = new SerializedObject(sourceObject, context); |
| var prop = obj.FindProperty(propertyPath); |
| if (prop == null) |
| throw new InvalidOperationException("sourceObject is not of the proper type. It does not contain a path to " + propertyPath); |
|
|
| Object result = null; |
| if (isSceneReference) |
| { |
| if (prop.propertyType != SerializedPropertyType.ExposedReference) |
| throw new InvalidOperationException(propertyPath + " is marked as a Scene Reference, but is not an exposed reference type"); |
| if (context == null) |
| Debug.LogWarning("ObjectReferenceField.Find " + " is called on a scene reference without a context, will always be null"); |
|
|
| result = prop.exposedReferenceValue; |
| } |
| else |
| { |
| if (prop.propertyType != SerializedPropertyType.ObjectReference) |
| throw new InvalidOperationException(propertyPath + "is marked as an asset reference, but is not an object reference type"); |
| result = prop.objectReferenceValue; |
| } |
|
|
| return result; |
| } |
|
|
| |
| |
| |
| public bool IsAssignable(Object obj) |
| { |
| if (obj == null) |
| return false; |
|
|
| |
| bool potentialMatch = type.IsAssignableFrom(obj.GetType()); |
|
|
| |
| if (!potentialMatch && typeof(Component).IsAssignableFrom(type) && obj is GameObject) |
| potentialMatch = ((GameObject)obj).GetComponent(type) != null; |
|
|
| return potentialMatch && isSceneReference == obj.IsSceneObject(); |
| } |
|
|
| |
| |
| |
| public bool Assign(ScriptableObject scriptableObject, Object value, IExposedPropertyTable exposedTable = null) |
| { |
| var serializedObject = new SerializedObject(scriptableObject, exposedTable as Object); |
| var property = serializedObject.FindProperty(propertyPath); |
| if (property == null) |
| return false; |
|
|
| |
| if (value is GameObject && typeof(Component).IsAssignableFrom(type)) |
| value = ((GameObject)value).GetComponent(type); |
|
|
| if (isSceneReference) |
| { |
| property.exposedReferenceValue = value; |
|
|
| |
| var component = exposedTable as Component; |
| if (component != null && !EditorApplication.isPlaying) |
| EditorSceneManager.MarkSceneDirty(component.gameObject.scene); |
| } |
| else |
| property.objectReferenceValue = value; |
|
|
| serializedObject.ApplyModifiedProperties(); |
| return true; |
| } |
| } |
| } |
|
|