A game about forced loneliness, made by TACStudios
at master 195 lines 7.8 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Reflection; 4using UnityEngine; 5using UnityEngine.Timeline; 6using UnityEditor; 7using UnityEditor.SceneManagement; 8using UnityEngine.Playables; 9using Object = UnityEngine.Object; 10 11namespace UnityEditor.Timeline 12{ 13 // Describes the object references on a ScriptableObject, ignoring script fields 14 struct ObjectReferenceField 15 { 16 public string propertyPath; 17 public bool isSceneReference; 18 public System.Type type; 19 20 private readonly static ObjectReferenceField[] None = new ObjectReferenceField[0]; 21 private readonly static Dictionary<System.Type, ObjectReferenceField[]> s_Cache = new Dictionary<System.Type, ObjectReferenceField[]>(); 22 23 public static ObjectReferenceField[] FindObjectReferences(System.Type type) 24 { 25 if (type == null) 26 return None; 27 28 if (type.IsAbstract || type.IsInterface) 29 return None; 30 31 if (!typeof(ScriptableObject).IsAssignableFrom(type)) 32 return None; 33 34 ObjectReferenceField[] result = null; 35 if (s_Cache.TryGetValue(type, out result)) 36 return result; 37 38 result = SearchForFields(type); 39 s_Cache[type] = result; 40 return result; 41 } 42 43 public static ObjectReferenceField[] FindObjectReferences<T>() where T : ScriptableObject, new() 44 { 45 return FindObjectReferences(typeof(T)); 46 } 47 48 private static ObjectReferenceField[] SearchForFields(System.Type t) 49 { 50 Object instance = ScriptableObject.CreateInstance(t); 51 var list = new List<ObjectReferenceField>(); 52 53 var serializableObject = new SerializedObject(instance); 54 var prop = serializableObject.GetIterator(); 55 bool enterChildren = true; 56 while (prop.NextVisible(enterChildren)) 57 { 58 enterChildren = true; 59 var ppath = prop.propertyPath; 60 if (ppath == "m_Script") 61 { 62 enterChildren = false; 63 } 64 else if (prop.propertyType == SerializedPropertyType.ObjectReference || prop.propertyType == SerializedPropertyType.ExposedReference) 65 { 66 enterChildren = false; 67 var exposedType = GetTypeFromPath(t, prop.propertyPath); 68 if (exposedType != null && typeof(Object).IsAssignableFrom(exposedType)) 69 { 70 bool isSceneRef = prop.propertyType == SerializedPropertyType.ExposedReference; 71 list.Add( 72 new ObjectReferenceField() { propertyPath = prop.propertyPath, isSceneReference = isSceneRef, type = exposedType } 73 ); 74 } 75 } 76 } 77 78 Object.DestroyImmediate(instance); 79 if (list.Count == 0) 80 return None; 81 return list.ToArray(); 82 } 83 84 private static System.Type GetTypeFromPath(System.Type baseType, string path) 85 { 86 if (string.IsNullOrEmpty(path)) 87 return null; 88 89 System.Type parentType = baseType; 90 FieldInfo field = null; 91 var pathTo = path.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); 92 var flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | 93 BindingFlags.Instance; 94 foreach (string s in pathTo) 95 { 96 field = parentType.GetField(s, flags); 97 while (field == null) 98 { 99 if (parentType.BaseType == null) 100 return null; // Should not happen really. Means SerializedObject got the property, but the reflection missed it 101 parentType = parentType.BaseType; 102 field = parentType.GetField(s, flags); 103 } 104 105 parentType = field.FieldType; 106 } 107 108 // dig out exposed reference types 109 if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(ExposedReference<Object>).GetGenericTypeDefinition()) 110 { 111 return field.FieldType.GetGenericArguments()[0]; 112 } 113 114 return field.FieldType; 115 } 116 117 public Object Find(ScriptableObject sourceObject, Object context = null) 118 { 119 if (sourceObject == null) 120 return null; 121 122 SerializedObject obj = new SerializedObject(sourceObject, context); 123 var prop = obj.FindProperty(propertyPath); 124 if (prop == null) 125 throw new InvalidOperationException("sourceObject is not of the proper type. It does not contain a path to " + propertyPath); 126 127 Object result = null; 128 if (isSceneReference) 129 { 130 if (prop.propertyType != SerializedPropertyType.ExposedReference) 131 throw new InvalidOperationException(propertyPath + " is marked as a Scene Reference, but is not an exposed reference type"); 132 if (context == null) 133 Debug.LogWarning("ObjectReferenceField.Find " + " is called on a scene reference without a context, will always be null"); 134 135 result = prop.exposedReferenceValue; 136 } 137 else 138 { 139 if (prop.propertyType != SerializedPropertyType.ObjectReference) 140 throw new InvalidOperationException(propertyPath + "is marked as an asset reference, but is not an object reference type"); 141 result = prop.objectReferenceValue; 142 } 143 144 return result; 145 } 146 147 /// <summary> 148 /// Check if an Object satisfies this field, including components 149 /// </summary> 150 public bool IsAssignable(Object obj) 151 { 152 if (obj == null) 153 return false; 154 155 // types match 156 bool potentialMatch = type.IsAssignableFrom(obj.GetType()); 157 158 // field is component, and it exists on the gameObject 159 if (!potentialMatch && typeof(Component).IsAssignableFrom(type) && obj is GameObject) 160 potentialMatch = ((GameObject)obj).GetComponent(type) != null; 161 162 return potentialMatch && isSceneReference == obj.IsSceneObject(); 163 } 164 165 /// <summary> 166 /// Assigns a value to the field 167 /// </summary> 168 public bool Assign(ScriptableObject scriptableObject, Object value, IExposedPropertyTable exposedTable = null) 169 { 170 var serializedObject = new SerializedObject(scriptableObject, exposedTable as Object); 171 var property = serializedObject.FindProperty(propertyPath); 172 if (property == null) 173 return false; 174 175 // if the value is a game object, but the field is a component 176 if (value is GameObject && typeof(Component).IsAssignableFrom(type)) 177 value = ((GameObject)value).GetComponent(type); 178 179 if (isSceneReference) 180 { 181 property.exposedReferenceValue = value; 182 183 // the object gets dirtied, but not the scene which is where the reference is stored 184 var component = exposedTable as Component; 185 if (component != null && !EditorApplication.isPlaying) 186 EditorSceneManager.MarkSceneDirty(component.gameObject.scene); 187 } 188 else 189 property.objectReferenceValue = value; 190 191 serializedObject.ApplyModifiedProperties(); 192 return true; 193 } 194 } 195}