123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- 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
- {
- // Describes the object references on a ScriptableObject, ignoring script fields
- 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; // Should not happen really. Means SerializedObject got the property, but the reflection missed it
- parentType = parentType.BaseType;
- field = parentType.GetField(s, flags);
- }
- parentType = field.FieldType;
- }
- // dig out exposed reference types
- 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;
- }
- /// <summary>
- /// Check if an Object satisfies this field, including components
- /// </summary>
- public bool IsAssignable(Object obj)
- {
- if (obj == null)
- return false;
- // types match
- bool potentialMatch = type.IsAssignableFrom(obj.GetType());
- // field is component, and it exists on the gameObject
- if (!potentialMatch && typeof(Component).IsAssignableFrom(type) && obj is GameObject)
- potentialMatch = ((GameObject)obj).GetComponent(type) != null;
- return potentialMatch && isSceneReference == obj.IsSceneObject();
- }
- /// <summary>
- /// Assigns a value to the field
- /// </summary>
- 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 the value is a game object, but the field is a component
- if (value is GameObject && typeof(Component).IsAssignableFrom(type))
- value = ((GameObject)value).GetComponent(type);
- if (isSceneReference)
- {
- property.exposedReferenceValue = value;
- // the object gets dirtied, but not the scene which is where the reference is stored
- var component = exposedTable as Component;
- if (component != null && !EditorApplication.isPlaying)
- EditorSceneManager.MarkSceneDirty(component.gameObject.scene);
- }
- else
- property.objectReferenceValue = value;
- serializedObject.ApplyModifiedProperties();
- return true;
- }
- }
- }
|