using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; using Object = UnityEngine.Object; namespace UnityEditor.Timeline { class PropertyCollector : IPropertyCollector { readonly Stack m_ObjectStack = new Stack(); // Call immediately before use public void Reset() { m_ObjectStack.Clear(); } // call to reset caches. should be called when switching master timelines public void Clear() { m_ObjectStack.Clear(); AnimationPreviewUtilities.ClearCaches(); } public void PushActiveGameObject(GameObject gameObject) { m_ObjectStack.Push(gameObject); } public void PopActiveGameObject() { m_ObjectStack.Pop(); } public void AddFromClip(AnimationClip clip) { var go = m_ObjectStack.Peek(); // allow it to throw if empty if (go != null && clip != null) // null game object is allowed for calls to be ignored AddFromClip(go, clip); } public void AddFromClips(IEnumerable clips) { var go = m_ObjectStack.Peek(); if (go != null) AddFromClips(go, clips); } public void AddFromName(string name) where T : Component { var go = m_ObjectStack.Peek(); // allow it to throw if empty if (go != null) // null game object is allowed for calls to be ignored AddFromName(go, name); } public void AddFromName(string name) { var go = m_ObjectStack.Peek(); // allow it to throw if empty if (go != null) // null game object is allowed for calls to be ignored AddFromName(go, name); } public void AddFromClip(GameObject obj, AnimationClip clip) { if (!Application.isPlaying) AddPropertiesFromClip(obj, clip); } public void AddFromClips(GameObject animatorRoot, IEnumerable clips) { if (Application.isPlaying) return; AnimationPreviewUtilities.PreviewFromCurves(animatorRoot, AnimationPreviewUtilities.GetBindings(animatorRoot, clips)); } public void AddFromName(GameObject obj, string name) where T : Component { if (!Application.isPlaying) AddPropertiesFromName(obj, typeof(T), name); } public void AddFromName(GameObject obj, string name) { if (!Application.isPlaying) AddPropertiesFromName(obj, name); } public void AddFromName(Component component, string name) { if (!Application.isPlaying) AddPropertyModification(component, name); } public void AddFromComponent(GameObject obj, Component component) { if (Application.isPlaying) return; if (obj == null || component == null) return; var serializedObject = new SerializedObject(component); SerializedProperty property = serializedObject.GetIterator(); while (property.NextVisible(true)) { if (property.hasVisibleChildren || !AnimatedParameterUtility.IsTypeAnimatable(property.propertyType)) continue; AddPropertyModification(component, property.propertyPath); } } void AddPropertiesFromClip(GameObject go, AnimationClip clip) { if (go != null && clip != null) { AnimationMode.InitializePropertyModificationForGameObject(go, clip); } } static void AddPropertiesFromName(GameObject go, string property) { if (go == null) return; AddPropertyModification(go, property); } static void AddPropertiesFromName(GameObject go, Type compType, string property) { if (go == null) return; var comp = go.GetComponent(compType); if (comp == null) return; AddPropertyModification(comp, property); } public void AddObjectProperties(Object obj, AnimationClip clip) { if (obj == null || clip == null) return; IPlayableAsset asset = obj as IPlayableAsset; IPlayableBehaviour playable = obj as IPlayableBehaviour; // special case for assets that contain animated script playables. // The paths in the clip start from the field with the templated playable if (asset != null) { if (playable == null) { AddSerializedPlayableModifications(asset, clip); } else { // in this case the asset is the playable. The clip applies directly AnimationMode.InitializePropertyModificationForObject(obj, clip); } } } void AddSerializedPlayableModifications(IPlayableAsset asset, AnimationClip clip) { var obj = asset as Object; if (obj == null) return; var driver = WindowState.previewDriver; if (driver == null || !AnimationMode.InAnimationMode(driver)) return; var serializedObj = new SerializedObject(obj); var bindings = AnimationClipCurveCache.Instance.GetCurveInfo(clip).bindings; var fields = AnimatedParameterUtility.GetScriptPlayableFields(asset); // go through each binding and offset using the field name // so the modification system can find the particle object using the asset as a root foreach (var b in bindings) { foreach (var f in fields) { var propertyPath = f.Name + "." + b.propertyName; if (serializedObj.FindProperty(propertyPath) != null) { DrivenPropertyManager.RegisterProperty(driver, obj, propertyPath); break; } } } } private static void AddPropertyModification(GameObject obj, string propertyName) { var driver = WindowState.previewDriver; if (driver == null || !AnimationMode.InAnimationMode(driver)) return; DrivenPropertyManager.RegisterProperty(driver, obj, propertyName); } private static void AddPropertyModification(Component comp, string name) { if (comp == null) return; var driver = WindowState.previewDriver; if (driver == null || !AnimationMode.InAnimationMode(driver)) return; // Register Property will display an error if a property doesn't exist (wanted behaviour) // However, it also displays an error on Monobehaviour m_Script property, since it can't be driven. (not wanted behaviour) // case 967026 if (name == "m_Script" && (comp as MonoBehaviour) != null) return; DrivenPropertyManager.RegisterProperty(driver, comp, name); } } }