UnityGame/Library/PackageCache/com.unity.timeline/Editor/Utilities/PropertyCollector.cs
2024-10-27 10:53:47 +03:00

227 lines
7.4 KiB
C#

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<GameObject> m_ObjectStack = new Stack<GameObject>();
// 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<AnimationClip> clips)
{
var go = m_ObjectStack.Peek();
if (go != null)
AddFromClips(go, clips);
}
public void AddFromName<T>(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<T>(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<AnimationClip> clips)
{
if (Application.isPlaying)
return;
AnimationPreviewUtilities.PreviewFromCurves(animatorRoot, AnimationPreviewUtilities.GetBindings(animatorRoot, clips));
}
public void AddFromName<T>(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);
}
}
}