Unity-jump-proj

This commit is contained in:
2024-09-09 11:07:16 +03:00
parent 2c29906bbf
commit fd96a5627d
13707 changed files with 866380 additions and 0 deletions

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace UnityEditor.Timeline
{
static class AnimatedParameterCache
{
static readonly Dictionary<Type, FieldInfo[]> k_ScriptPlayableFieldsCache = new Dictionary<Type, FieldInfo[]>();
static readonly Dictionary<PropertyKey, FieldInfo> k_PropertyFieldInfoCache = new Dictionary<PropertyKey, FieldInfo>();
static readonly Dictionary<PropertyKey, bool> k_PropertyIsAnimatableCache = new Dictionary<PropertyKey, bool>();
static readonly Dictionary<PropertyKey, string> k_BindingNameCache = new Dictionary<PropertyKey, string>();
public static bool TryGetScriptPlayableFields(Type type, out FieldInfo[] scriptPlayableFields)
{
return k_ScriptPlayableFieldsCache.TryGetValue(type, out scriptPlayableFields);
}
public static void SetScriptPlayableFields(Type type, FieldInfo[] scriptPlayableFields)
{
k_ScriptPlayableFieldsCache[type] = scriptPlayableFields;
}
public static bool TryGetFieldInfoForProperty(SerializedProperty property, out FieldInfo fieldInfo)
{
return k_PropertyFieldInfoCache.TryGetValue(new PropertyKey(property), out fieldInfo);
}
public static void SetFieldInfoForProperty(SerializedProperty property, FieldInfo fieldInfo)
{
k_PropertyFieldInfoCache[new PropertyKey(property)] = fieldInfo;
}
public static bool TryGetIsPropertyAnimatable(SerializedProperty property, out bool isAnimatable)
{
return k_PropertyIsAnimatableCache.TryGetValue(new PropertyKey(property), out isAnimatable);
}
public static void SetIsPropertyAnimatable(SerializedProperty property, bool isAnimatable)
{
k_PropertyIsAnimatableCache[new PropertyKey(property)] = isAnimatable;
}
public static bool TryGetBindingName(Type type, string path, out string bindingName)
{
return k_BindingNameCache.TryGetValue(new PropertyKey(type, path), out bindingName);
}
public static void SetBindingName(Type type, string path, string bindingName)
{
k_BindingNameCache[new PropertyKey(type, path)] = bindingName;
}
}
struct PropertyKey : IEquatable<PropertyKey>
{
readonly Type m_Type;
readonly string m_Path;
public PropertyKey(SerializedProperty property)
{
m_Type = property.serializedObject.targetObject.GetType();
m_Path = property.propertyPath;
}
public PropertyKey(Type type, string path)
{
m_Type = type;
m_Path = path;
}
public bool Equals(PropertyKey other)
{
return m_Type == other.m_Type && string.Equals(m_Path, other.m_Path);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is PropertyKey && Equals((PropertyKey)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((m_Type != null ? m_Type.GetHashCode() : 0) * 397) ^ (m_Path != null ? m_Path.GetHashCode() : 0);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1fe0f539450e54dbc85bfb2fa6b466fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,358 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Timeline
{
static class AnimatedParameterUtility
{
static readonly Type k_DefaultAnimationType = typeof(TimelineAsset);
static SerializedObject s_CachedObject;
public static ICurvesOwner ToCurvesOwner(IPlayableAsset playableAsset, TimelineAsset timeline)
{
if (playableAsset == null)
return null;
var curvesOwner = playableAsset as ICurvesOwner;
if (curvesOwner == null)
{
// If the asset is not directly an ICurvesOwner, it might be the asset for a TimelineClip
curvesOwner = TimelineRecording.FindClipWithAsset(timeline, playableAsset);
}
return curvesOwner;
}
public static bool TryGetSerializedPlayableAsset(UnityObject asset, out SerializedObject serializedObject)
{
serializedObject = null;
if (asset == null || Attribute.IsDefined(asset.GetType(), typeof(NotKeyableAttribute)) || !HasScriptPlayable(asset))
return false;
serializedObject = GetSerializedPlayableAsset(asset);
return serializedObject != null;
}
public static SerializedObject GetSerializedPlayableAsset(UnityObject asset)
{
if (!(asset is IPlayableAsset))
return null;
var scriptObject = asset as ScriptableObject;
if (scriptObject == null)
return null;
if (s_CachedObject == null || s_CachedObject.targetObject != asset)
{
s_CachedObject = new SerializedObject(scriptObject);
}
return s_CachedObject;
}
public static void UpdateSerializedPlayableAsset(UnityObject asset)
{
var so = GetSerializedPlayableAsset(asset);
if (so != null)
so.UpdateIfRequiredOrScript();
}
public static bool HasScriptPlayable(UnityObject asset)
{
if (asset == null)
return false;
var scriptPlayable = asset as IPlayableBehaviour;
return scriptPlayable != null || GetScriptPlayableFields(asset as IPlayableAsset).Any();
}
public static FieldInfo[] GetScriptPlayableFields(IPlayableAsset asset)
{
if (asset == null)
return new FieldInfo[0];
FieldInfo[] scriptPlayableFields;
if (!AnimatedParameterCache.TryGetScriptPlayableFields(asset.GetType(), out scriptPlayableFields))
{
scriptPlayableFields = GetScriptPlayableFields_Internal(asset);
AnimatedParameterCache.SetScriptPlayableFields(asset.GetType(), scriptPlayableFields);
}
return scriptPlayableFields;
}
static FieldInfo[] GetScriptPlayableFields_Internal(IPlayableAsset asset)
{
return asset.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(
f => typeof(IPlayableBehaviour).IsAssignableFrom(f.FieldType) && // The field is an IPlayableBehaviour
(f.IsPublic || f.GetCustomAttributes(typeof(SerializeField), false).Any()) && // The field is either public or marked with [SerializeField]
!f.GetCustomAttributes(typeof(NotKeyableAttribute), false).Any() && // The field is not marked with [NotKeyable]
!f.GetCustomAttributes(typeof(HideInInspector), false).Any() && // The field is not marked with [HideInInspector]
!f.FieldType.GetCustomAttributes(typeof(NotKeyableAttribute), false).Any()) // The field is not of a type marked with [NotKeyable]
.ToArray();
}
public static bool HasAnyAnimatableParameters(UnityObject asset)
{
return GetAllAnimatableParameters(asset).Any();
}
public static IEnumerable<SerializedProperty> GetAllAnimatableParameters(UnityObject asset)
{
SerializedObject serializedObject;
if (!TryGetSerializedPlayableAsset(asset, out serializedObject))
yield break;
var prop = serializedObject.GetIterator();
// We need to keep this variable because prop starts invalid
var outOfBounds = false;
while (!outOfBounds && prop.NextVisible(true))
{
foreach (var property in SelectAnimatableProperty(prop))
yield return property;
// We can become out of bounds by calling SelectAnimatableProperty, if the last iterated property is a color.
outOfBounds = !prop.isValid;
}
}
static IEnumerable<SerializedProperty> SelectAnimatableProperty(SerializedProperty prop)
{
// We're only interested by animatable leaf parameters
if (!prop.hasChildren && IsParameterAnimatable(prop))
yield return prop.Copy();
// Color type is not considered "visible" when iterating
if (prop.propertyType == SerializedPropertyType.Color)
{
var end = prop.GetEndProperty();
// For some reasons, if the last 2+ serialized properties are of type Color, prop becomes invalid and
// Next() throws an exception. This is not the case when only the last serialized property is a Color.
while (!SerializedProperty.EqualContents(prop, end) && prop.isValid && prop.Next(true))
{
foreach (var property in SelectAnimatableProperty(prop))
yield return property;
}
}
}
public static bool IsParameterAnimatable(UnityObject asset, string parameterName)
{
SerializedObject serializedObject;
if (!TryGetSerializedPlayableAsset(asset, out serializedObject))
return false;
var prop = serializedObject.FindProperty(parameterName);
return IsParameterAnimatable(prop);
}
public static bool IsParameterAnimatable(SerializedProperty property)
{
if (property == null)
return false;
bool isAnimatable;
if (!AnimatedParameterCache.TryGetIsPropertyAnimatable(property, out isAnimatable))
{
isAnimatable = IsParameterAnimatable_Internal(property);
AnimatedParameterCache.SetIsPropertyAnimatable(property, isAnimatable);
}
return isAnimatable;
}
static bool IsParameterAnimatable_Internal(SerializedProperty property)
{
if (property == null)
return false;
var asset = property.serializedObject.targetObject;
// Currently not supported
if (asset is AnimationTrack)
return false;
if (IsParameterKeyable(property))
return asset is IPlayableBehaviour || IsParameterAtPathAnimatable(asset, property.propertyPath);
return false;
}
static bool IsParameterKeyable(SerializedProperty property)
{
return IsTypeAnimatable(property.propertyType) && IsKeyableInHierarchy(property);
}
static bool IsKeyableInHierarchy(SerializedProperty property)
{
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var pathSegments = property.propertyPath.Split('.');
var type = property.serializedObject.targetObject.GetType();
foreach (var segment in pathSegments)
{
if (type.GetCustomAttributes(typeof(NotKeyableAttribute), false).Any())
{
return false;
}
if (type.IsArray)
return false;
var fieldInfo = type.GetField(segment, bindingFlags);
if (fieldInfo == null ||
fieldInfo.GetCustomAttributes(typeof(NotKeyableAttribute), false).Any() ||
fieldInfo.GetCustomAttributes(typeof(HideInInspector), false).Any())
{
return false;
}
type = fieldInfo.FieldType;
// only value types are supported
if (!type.IsValueType && !typeof(IPlayableBehaviour).IsAssignableFrom(type))
return false;
}
return true;
}
static bool IsParameterAtPathAnimatable(UnityObject asset, string path)
{
if (asset == null)
return false;
return GetScriptPlayableFields(asset as IPlayableAsset)
.Any(
f => path.StartsWith(f.Name, StringComparison.Ordinal) &&
path.Length > f.Name.Length &&
path[f.Name.Length] == '.');
}
public static bool IsTypeAnimatable(SerializedPropertyType type)
{
// Note: Integer is not currently supported by the animated property system
switch (type)
{
case SerializedPropertyType.Boolean:
case SerializedPropertyType.Float:
case SerializedPropertyType.Vector2:
case SerializedPropertyType.Vector3:
case SerializedPropertyType.Color:
case SerializedPropertyType.Quaternion:
case SerializedPropertyType.Vector4:
return true;
default:
return false;
}
}
public static bool IsParameterAnimated(UnityObject asset, AnimationClip animationData, string parameterName)
{
if (asset == null || animationData == null)
return false;
var binding = GetCurveBinding(asset, parameterName);
var bindings = AnimationClipCurveCache.Instance.GetCurveInfo(animationData).bindings;
return bindings.Any(x => BindingMatchesParameterName(x, binding.propertyName));
}
// Retrieve an animated parameter curve. parameter name is required to include the appropriate field for vectors
// e.g.: position
public static AnimationCurve GetAnimatedParameter(UnityObject asset, AnimationClip animationData, string parameterName)
{
if (!(asset is ScriptableObject) || animationData == null)
return null;
var binding = GetCurveBinding(asset, parameterName);
return AnimationUtility.GetEditorCurve(animationData, binding);
}
// get an animatable curve binding for this parameter
public static EditorCurveBinding GetCurveBinding(UnityObject asset, string parameterName)
{
var animationName = GetAnimatedParameterBindingName(asset, parameterName);
return EditorCurveBinding.FloatCurve(string.Empty, GetValidAnimationType(asset), animationName);
}
public static string GetAnimatedParameterBindingName(UnityObject asset, string parameterName)
{
if (asset == null)
return parameterName;
string bindingName;
if (!AnimatedParameterCache.TryGetBindingName(asset.GetType(), parameterName, out bindingName))
{
bindingName = GetAnimatedParameterBindingName_Internal(asset, parameterName);
AnimatedParameterCache.SetBindingName(asset.GetType(), parameterName, bindingName);
}
return bindingName;
}
static string GetAnimatedParameterBindingName_Internal(UnityObject asset, string parameterName)
{
if (asset is IPlayableBehaviour)
return parameterName;
// strip the IScript playable field name
var fields = GetScriptPlayableFields(asset as IPlayableAsset);
foreach (var f in fields)
{
if (parameterName.StartsWith(f.Name, StringComparison.Ordinal))
{
if (parameterName.Length > f.Name.Length && parameterName[f.Name.Length] == '.')
return parameterName.Substring(f.Name.Length + 1);
}
}
return parameterName;
}
public static bool BindingMatchesParameterName(EditorCurveBinding binding, string parameterName)
{
if (binding.propertyName == parameterName)
return true;
var indexOfDot = binding.propertyName.LastIndexOf('.');
return indexOfDot > 0 && parameterName.Length == indexOfDot &&
binding.propertyName.StartsWith(parameterName, StringComparison.Ordinal);
}
// the animated type must be a non-abstract instantiable object.
public static Type GetValidAnimationType(UnityObject asset)
{
return asset != null ? asset.GetType() : k_DefaultAnimationType;
}
public static FieldInfo GetFieldInfoForProperty(SerializedProperty property)
{
FieldInfo fieldInfo;
if (!AnimatedParameterCache.TryGetFieldInfoForProperty(property, out fieldInfo))
{
Type _;
fieldInfo = ScriptAttributeUtility.GetFieldInfoFromProperty(property, out _);
AnimatedParameterCache.SetFieldInfoForProperty(property, fieldInfo);
}
return fieldInfo;
}
public static T GetAttributeForProperty<T>(SerializedProperty property) where T : Attribute
{
var fieldInfo = GetFieldInfoForProperty(property);
return fieldInfo.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84d86c98104d94063ad70bc591530f65
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,73 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
// Helper methods for animated properties
internal static class AnimatedPropertyUtility
{
public static bool IsMaterialProperty(string propertyName)
{
return propertyName.StartsWith("material.");
}
/// <summary>
/// Given a propertyName (from an EditorCurveBinding), and the gameObject it refers to,
/// remaps the path to include the exposed name of the shader parameter
/// </summary>
/// <param name="gameObject">The gameObject being referenced.</param>
/// <param name="propertyName">The propertyName to remap.</param>
/// <returns>The remapped propertyName, or the original propertyName if it cannot be remapped</returns>
public static string RemapMaterialName(GameObject gameObject, string propertyName)
{
if (!IsMaterialProperty(propertyName) || gameObject == null)
return propertyName;
var renderers = gameObject.GetComponents<Renderer>();
if (renderers == null || renderers.Length == 0)
return propertyName;
var propertySplits = propertyName.Split('.');
if (propertySplits.Length <= 1)
return propertyName;
// handles post fixes for texture properties
var exposedParameter = HandleTextureProperties(propertySplits[1], out var postFix);
foreach (var renderer in renderers)
{
foreach (var material in renderer.sharedMaterials)
{
if (material.shader == null)
continue;
var index = material.shader.FindPropertyIndex(exposedParameter);
if (index >= 0)
{
propertySplits[1] = material.shader.GetPropertyDescription(index) + postFix;
return String.Join(".", propertySplits);
}
}
}
return propertyName;
}
private static string HandleTextureProperties(string exposedParameter, out string postFix)
{
postFix = String.Empty;
RemoveEnding(ref exposedParameter, ref postFix, "_ST");
RemoveEnding(ref exposedParameter, ref postFix, "_TexelSize");
RemoveEnding(ref exposedParameter, ref postFix, "_HDR");
return exposedParameter;
}
private static void RemoveEnding(ref string name, ref string postFix, string ending)
{
if (name.EndsWith(ending))
{
name = name.Substring(0, name.Length - ending.Length);
postFix = ending;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e22173cccd50433098692e06d6811255
timeCreated: 1602163952

View File

@ -0,0 +1,130 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
static class BindingUtility
{
public enum BindingAction
{
DoNotBind,
BindDirectly,
BindToExistingComponent,
BindToMissingComponent
}
const string k_BindingOperation = "Bind Track";
public static void Bind(PlayableDirector director, TrackAsset bindTo, Object objectToBind)
{
if (director == null || bindTo == null || TimelineWindow.instance == null)
return;
if (director.GetGenericBinding(bindTo) == objectToBind)
return;
TimelineWindow.instance.state.previewMode = false; // returns all objects to previous state
TimelineUndo.PushUndo(director, k_BindingOperation);
director.SetGenericBinding(bindTo, objectToBind);
TimelineWindow.instance.state.rebuildGraph = true;
}
public static void BindWithEditorValidation(PlayableDirector director, TrackAsset bindTo, Object objectToBind)
{
TrackEditor trackEditor = CustomTimelineEditorCache.GetTrackEditor(bindTo);
Object validatedObject = trackEditor.GetBindingFrom_Safe(objectToBind, bindTo);
Bind(director, bindTo, validatedObject);
}
public static void BindWithInteractiveEditorValidation(PlayableDirector director, TrackAsset bindTo, Object objectToBind)
{
TrackEditor trackEditor = CustomTimelineEditorCache.GetTrackEditor(bindTo);
if (trackEditor.SupportsBindingAssign())
BindWithEditorValidation(director, bindTo, objectToBind);
else
{
Type bindingType = TypeUtility.GetTrackBindingAttribute(bindTo.GetType())?.type;
BindingAction action = GetBindingAction(bindingType, objectToBind);
if (action == BindingAction.BindToMissingComponent)
InteractiveBindToMissingComponent(director, bindTo, objectToBind, bindingType);
else
{
var validatedObject = GetBinding(action, objectToBind, bindingType);
Bind(director, bindTo, validatedObject);
}
}
}
public static BindingAction GetBindingAction(Type requiredBindingType, Object objectToBind)
{
if (requiredBindingType == null || objectToBind == null)
return BindingAction.DoNotBind;
// prevent drag and drop of prefab assets
if (PrefabUtility.IsPartOfPrefabAsset(objectToBind))
return BindingAction.DoNotBind;
if (requiredBindingType.IsInstanceOfType(objectToBind))
return BindingAction.BindDirectly;
var draggedGameObject = objectToBind as GameObject;
if (!typeof(Component).IsAssignableFrom(requiredBindingType) || draggedGameObject == null)
return BindingAction.DoNotBind;
if (draggedGameObject.GetComponent(requiredBindingType) == null)
return BindingAction.BindToMissingComponent;
return BindingAction.BindToExistingComponent;
}
public static Object GetBinding(BindingAction bindingAction, Object objectToBind, Type requiredBindingType)
{
if (objectToBind == null) return null;
switch (bindingAction)
{
case BindingAction.BindDirectly:
{
return objectToBind;
}
case BindingAction.BindToExistingComponent:
{
var gameObjectBeingDragged = objectToBind as GameObject;
Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject");
return gameObjectBeingDragged.GetComponent(requiredBindingType);
}
case BindingAction.BindToMissingComponent:
{
var gameObjectBeingDragged = objectToBind as GameObject;
Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject");
return Undo.AddComponent(gameObjectBeingDragged, requiredBindingType);
}
default:
return null;
}
}
static void InteractiveBindToMissingComponent(PlayableDirector director, TrackAsset bindTo, Object objectToBind, Type requiredComponentType)
{
var gameObjectBeingDragged = objectToBind as GameObject;
Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject");
string typeNameOfComponent = requiredComponentType.ToString().Split(".".ToCharArray()).Last();
var bindMenu = new GenericMenu();
bindMenu.AddItem(
EditorGUIUtility.TextContent("Create " + typeNameOfComponent + " on " + gameObjectBeingDragged.name),
false,
nullParam => Bind(director, bindTo, Undo.AddComponent(gameObjectBeingDragged, requiredComponentType)),
null);
bindMenu.AddSeparator("");
bindMenu.AddItem(EditorGUIUtility.TrTextContent("Cancel"), false, userData => { }, null);
bindMenu.ShowAsContext();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef5fa6e2005defb4ab5142723827b58e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
#else
using UnityEditor.Experimental.SceneManagement;
#endif
using UnityEngine;
namespace UnityEditor.Timeline
{
enum TitleMode
{
None,
DisabledComponent,
Prefab,
PrefabOutOfContext,
Asset,
GameObject
}
struct BreadCrumbTitle
{
public string name;
public TitleMode mode;
}
class BreadcrumbDrawer
{
static readonly GUIContent s_TextContent = new GUIContent();
static readonly string k_DisabledComponentText = L10n.Tr("The PlayableDirector is disabled");
static readonly string k_PrefabOutOfContext = L10n.Tr("Prefab Isolation not enabled. Click to Enable.");
static readonly GUIStyle k_BreadCrumbLeft;
static readonly GUIStyle k_BreadCrumbMid;
static readonly GUIStyle k_BreadCrumbLeftBg;
static readonly GUIStyle k_BreadCrumbMidBg;
static readonly GUIStyle k_BreadCrumbMidSelected;
static readonly GUIStyle k_BreadCrumbMidBgSelected;
static readonly Texture k_TimelineIcon;
const string k_Elipsis = "…";
static BreadcrumbDrawer()
{
k_BreadCrumbLeft = "GUIEditor.BreadcrumbLeft";
k_BreadCrumbMid = "GUIEditor.BreadcrumbMid";
k_BreadCrumbLeftBg = "GUIEditor.BreadcrumbLeftBackground";
k_BreadCrumbMidBg = "GUIEditor.BreadcrumbMidBackground";
k_BreadCrumbMidSelected = k_BreadCrumbMid;
k_BreadCrumbMidSelected.normal = k_BreadCrumbMidSelected.onNormal;
k_BreadCrumbMidBgSelected = k_BreadCrumbMidBg;
k_BreadCrumbMidBgSelected.normal = k_BreadCrumbMidBgSelected.onNormal;
k_TimelineIcon = EditorGUIUtility.IconContent("TimelineAsset Icon").image;
}
static string FitTextInArea(float areaWidth, string text, GUIStyle style)
{
var borderWidth = style.border.left + style.border.right;
var textWidth = style.CalcSize(EditorGUIUtility.TextContent(text)).x;
if (borderWidth + textWidth < areaWidth)
return text;
// Need to truncate the text to fit in the areaWidth
var textAreaWidth = areaWidth - borderWidth;
var pixByChar = textWidth / text.Length;
var charNeeded = (int)Mathf.Floor(textAreaWidth / pixByChar);
charNeeded -= k_Elipsis.Length;
if (charNeeded <= 0)
return k_Elipsis;
if (charNeeded <= text.Length)
return k_Elipsis + " " + text.Substring(text.Length - charNeeded);
return k_Elipsis;
}
public static void Draw(float breadcrumbAreaWidth, List<BreadCrumbTitle> labels, Action<int> navigateToBreadcrumbIndex)
{
GUILayout.BeginHorizontal();
{
var labelWidth = (int)(breadcrumbAreaWidth / labels.Count);
for (var i = 0; i < labels.Count; i++)
{
var label = labels[i];
var style = i == 0 ? k_BreadCrumbLeft : k_BreadCrumbMid;
var backgroundStyle = i == 0 ? k_BreadCrumbLeftBg : k_BreadCrumbMidBg;
if (i == labels.Count - 1)
{
if (i > 0) // Only tint last breadcrumb if we are dug-in
DrawBreadcrumbAsSelectedSubSequence(labelWidth, label, k_BreadCrumbMidSelected, k_BreadCrumbMidBgSelected);
else
DrawActiveBreadcrumb(labelWidth, label, style, backgroundStyle);
}
else
{
var previousContentColor = GUI.contentColor;
GUI.contentColor = new Color(previousContentColor.r,
previousContentColor.g,
previousContentColor.b,
previousContentColor.a * 0.6f);
var content = GetTextContent(labelWidth, label, style);
var rect = GetBreadcrumbLayoutRect(content, style);
if (Event.current.type == EventType.Repaint)
backgroundStyle.Draw(rect, GUIContent.none, 0);
if (GUI.Button(rect, content, style))
navigateToBreadcrumbIndex.Invoke(i);
GUI.contentColor = previousContentColor;
}
}
}
GUILayout.EndHorizontal();
}
static GUIContent GetTextContent(int width, BreadCrumbTitle text, GUIStyle style)
{
s_TextContent.tooltip = string.Empty;
s_TextContent.image = null;
if (text.mode == TitleMode.DisabledComponent)
{
s_TextContent.tooltip = k_DisabledComponentText;
s_TextContent.image = EditorGUIUtility.GetHelpIcon(MessageType.Warning);
}
else if (text.mode == TitleMode.Prefab)
s_TextContent.image = PrefabUtility.GameObjectStyles.prefabIcon;
else if (text.mode == TitleMode.GameObject)
s_TextContent.image = PrefabUtility.GameObjectStyles.gameObjectIcon;
else if (text.mode == TitleMode.Asset)
s_TextContent.image = k_TimelineIcon;
else if (text.mode == TitleMode.PrefabOutOfContext)
{
s_TextContent.image = PrefabUtility.GameObjectStyles.prefabIcon;
if (!TimelineWindow.instance.locked)
s_TextContent.tooltip = k_PrefabOutOfContext;
}
if (s_TextContent.image != null)
width = Math.Max(0, width - s_TextContent.image.width);
s_TextContent.text = FitTextInArea(width, text.name, style);
return s_TextContent;
}
static void DrawBreadcrumbAsSelectedSubSequence(int width, BreadCrumbTitle label, GUIStyle style, GUIStyle backgroundStyle)
{
var rect = DrawActiveBreadcrumb(width, label, style, backgroundStyle);
const float underlineThickness = 2.0f;
const float underlineVerticalOffset = 0.0f;
var underlineHorizontalOffset = backgroundStyle.border.right * 0.333f;
var underlineRect = Rect.MinMaxRect(
rect.xMin - underlineHorizontalOffset,
rect.yMax - underlineThickness - underlineVerticalOffset,
rect.xMax - underlineHorizontalOffset,
rect.yMax - underlineVerticalOffset);
EditorGUI.DrawRect(underlineRect, DirectorStyles.Instance.customSkin.colorSubSequenceDurationLine);
}
static Rect GetBreadcrumbLayoutRect(GUIContent content, GUIStyle style)
{
// the image makes the button far too big compared to non-image versions
var image = content.image;
content.image = null;
var size = style.CalcSizeWithConstraints(content, Vector2.zero);
content.image = image;
if (image != null)
size.x += size.y; // assumes square image, constrained by height
return GUILayoutUtility.GetRect(content, style, GUILayout.MaxWidth(size.x));
}
static Rect DrawActiveBreadcrumb(int width, BreadCrumbTitle label, GUIStyle style, GUIStyle backgroundStyle)
{
var content = GetTextContent(width, label, style);
var rect = GetBreadcrumbLayoutRect(content, style);
if (Event.current.type == EventType.Repaint)
{
backgroundStyle.Draw(rect, GUIContent.none, 0);
}
if (GUI.Button(rect, content, style))
{
UnityEngine.Object target = TimelineEditor.inspectedDirector;
if (target == null)
target = TimelineEditor.inspectedAsset;
if (target != null)
{
bool ping = true;
if (label.mode == TitleMode.PrefabOutOfContext)
{
var gameObject = PrefabUtility.GetRootGameObject(target);
if (gameObject != null)
{
target = gameObject; // ping the prefab root if it's locked.
if (!TimelineWindow.instance.locked)
{
var assetPath = AssetDatabase.GetAssetPath(gameObject);
if (!string.IsNullOrEmpty(assetPath))
{
var stage = PrefabStageUtility.OpenPrefab(assetPath);
if (stage != null)
ping = false;
}
}
}
}
if (ping)
{
EditorGUIUtility.PingObject(target);
}
}
}
return rect;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 526f285e8d4fb8140b4cdfeb9102d8cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,397 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Timeline;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
static class ClipModifier
{
public static bool Delete(TimelineAsset timeline, TimelineClip clip)
{
return timeline.DeleteClip(clip);
}
public static bool Tile(IEnumerable<TimelineClip> clips)
{
if (clips.Count() < 2)
return false;
var clipsByTracks = clips.GroupBy(x => x.GetParentTrack())
.Select(track => new { track.Key, Items = track.OrderBy(c => c.start) });
foreach (var track in clipsByTracks)
{
UndoExtensions.RegisterTrack(track.Key, L10n.Tr("Tile"));
}
foreach (var track in clipsByTracks)
{
double newStart = track.Items.First().start;
foreach (var c in track.Items)
{
c.start = newStart;
newStart += c.duration;
}
}
return true;
}
public static bool TrimStart(IEnumerable<TimelineClip> clips, double trimTime)
{
var result = false;
foreach (var clip in clips)
result |= TrimStart(clip, trimTime);
return result;
}
public static bool TrimStart(TimelineClip clip, double trimTime)
{
if (clip.asset == null)
return false;
if (clip.start > trimTime)
return false;
if (clip.end < trimTime)
return false;
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip Start"));
// Note: We are NOT using edit modes in this case because we want the same result
// regardless of the selected EditMode: split at cursor and delete left part
SetStart(clip, trimTime, false);
clip.ConformEaseValues();
return true;
}
public static bool TrimEnd(IEnumerable<TimelineClip> clips, double trimTime)
{
var result = false;
foreach (var clip in clips)
result |= TrimEnd(clip, trimTime);
return result;
}
public static bool TrimEnd(TimelineClip clip, double trimTime)
{
if (clip.asset == null)
return false;
if (clip.start > trimTime)
return false;
if (clip.end < trimTime)
return false;
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip End"));
TrimClipWithEditMode(clip, TrimEdge.End, trimTime);
return true;
}
public static bool MatchDuration(IEnumerable<TimelineClip> clips)
{
double referenceDuration = clips.First().duration;
UndoExtensions.RegisterClips(clips, L10n.Tr("Match Clip Duration"));
foreach (var clip in clips)
{
var newEnd = clip.start + referenceDuration;
TrimClipWithEditMode(clip, TrimEdge.End, newEnd);
}
return true;
}
public static bool Split(IEnumerable<TimelineClip> clips, double splitTime, PlayableDirector director)
{
var result = false;
foreach (var clip in clips)
{
if (clip.start >= splitTime)
continue;
if (clip.end <= splitTime)
continue;
UndoExtensions.RegisterClip(clip, L10n.Tr("Split Clip"));
TimelineClip newClip = TimelineHelpers.Clone(clip, director, director, clip.start);
clip.easeInDuration = 0;
newClip.easeOutDuration = 0;
SetStart(clip, splitTime, false);
SetEnd(newClip, splitTime, false);
// Sort produced by cloning clips on top of each other is unpredictable (it varies between mono runtimes)
clip.GetParentTrack().SortClips();
result = true;
}
return result;
}
public static void SetStart(TimelineClip clip, double time, bool affectTimeScale)
{
var supportsClipIn = clip.SupportsClipIn();
var supportsPadding = TimelineUtility.IsRecordableAnimationClip(clip);
bool calculateTimeScale = (affectTimeScale && clip.SupportsSpeedMultiplier());
// treat empty recordable clips as not supporting clip in (there are no keys to modify)
if (supportsPadding && (clip.animationClip == null || clip.animationClip.empty))
{
supportsClipIn = false;
}
if (supportsClipIn && !supportsPadding && !calculateTimeScale)
{
var minStart = clip.FromLocalTimeUnbound(0.0);
if (time < minStart)
time = minStart;
}
var maxStart = clip.end - TimelineClip.kMinDuration;
if (time > maxStart)
time = maxStart;
var timeOffset = time - clip.start;
var duration = clip.duration - timeOffset;
if (calculateTimeScale)
{
var f = clip.duration / duration;
clip.timeScale *= f;
}
if (supportsClipIn && !calculateTimeScale)
{
if (supportsPadding)
{
double clipInGlobal = clip.clipIn / clip.timeScale;
double keyShift = -timeOffset;
if (timeOffset < 0) // left drag, eliminate clipIn before shifting
{
double clipInDelta = Math.Max(-clipInGlobal, timeOffset);
keyShift = -Math.Min(0, timeOffset - clipInDelta);
clip.clipIn += clipInDelta * clip.timeScale;
}
else if (timeOffset > 0) // right drag, elimate padding in animation clip before adding clip in
{
var clipInfo = AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip);
double keyDelta = clip.FromLocalTimeUnbound(clipInfo.keyTimes.Min()) - clip.start;
keyShift = -Math.Max(0, Math.Min(timeOffset, keyDelta));
clip.clipIn += Math.Max(timeOffset + keyShift, 0) * clip.timeScale;
}
if (keyShift != 0)
{
AnimationTrackRecorder.ShiftAnimationClip(clip.animationClip, (float)(keyShift * clip.timeScale));
}
}
else
{
clip.clipIn += timeOffset * clip.timeScale;
}
}
clip.start = time;
clip.duration = duration;
}
public static void SetEnd(TimelineClip clip, double time, bool affectTimeScale)
{
var duration = Math.Max(time - clip.start, TimelineClip.kMinDuration);
if (affectTimeScale && clip.SupportsSpeedMultiplier())
{
var f = clip.duration / duration;
clip.timeScale *= f;
}
clip.duration = duration;
}
public static bool ResetEditing(IEnumerable<TimelineClip> clips)
{
var result = false;
foreach (var clip in clips)
result = result || ResetEditing(clip);
return result;
}
public static bool ResetEditing(TimelineClip clip)
{
if (clip.asset == null)
return false;
UndoExtensions.RegisterClip(clip, L10n.Tr("Reset Clip Editing"));
clip.clipIn = 0.0;
if (clip.clipAssetDuration < double.MaxValue)
{
var duration = clip.clipAssetDuration / clip.timeScale;
TrimClipWithEditMode(clip, TrimEdge.End, clip.start + duration);
}
return true;
}
public static bool MatchContent(IEnumerable<TimelineClip> clips)
{
var result = false;
foreach (var clip in clips)
result |= MatchContent(clip);
return result;
}
public static bool MatchContent(TimelineClip clip)
{
if (clip.asset == null)
return false;
UndoExtensions.RegisterClip(clip, L10n.Tr("Match Clip Content"));
var newStartCandidate = clip.start - clip.clipIn / clip.timeScale;
var newStart = newStartCandidate < 0.0 ? 0.0 : newStartCandidate;
TrimClipWithEditMode(clip, TrimEdge.Start, newStart);
// In case resetting the start was blocked by edit mode or timeline start, we do the best we can
clip.clipIn = (clip.start - newStartCandidate) * clip.timeScale;
if (clip.clipAssetDuration > 0 && TimelineHelpers.HasUsableAssetDuration(clip))
{
var duration = TimelineHelpers.GetLoopDuration(clip);
var offset = (clip.clipIn / clip.timeScale) % duration;
TrimClipWithEditMode(clip, TrimEdge.End, clip.start - offset + duration);
}
return true;
}
public static void TrimClipWithEditMode(TimelineClip clip, TrimEdge edge, double time)
{
var clipItem = ItemsUtils.ToItem(clip);
EditMode.BeginTrim(clipItem, edge);
if (edge == TrimEdge.Start)
EditMode.TrimStart(clipItem, time, false);
else
EditMode.TrimEnd(clipItem, time, false);
EditMode.FinishTrim();
}
public static bool CompleteLastLoop(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
CompleteLastLoop(clip);
}
return true;
}
public static void CompleteLastLoop(TimelineClip clip)
{
FixLoops(clip, true);
}
public static bool TrimLastLoop(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
TrimLastLoop(clip);
}
return true;
}
public static void TrimLastLoop(TimelineClip clip)
{
FixLoops(clip, false);
}
static void FixLoops(TimelineClip clip, bool completeLastLoop)
{
if (!TimelineHelpers.HasUsableAssetDuration(clip))
return;
var loopDuration = TimelineHelpers.GetLoopDuration(clip);
var firstLoopDuration = loopDuration - clip.clipIn * (1.0 / clip.timeScale);
// Making sure we don't trim to zero
if (!completeLastLoop && firstLoopDuration > clip.duration)
return;
var numLoops = (clip.duration - firstLoopDuration) / loopDuration;
var numCompletedLoops = Math.Floor(numLoops);
if (!(numCompletedLoops < numLoops))
return;
if (completeLastLoop)
numCompletedLoops += 1;
var newEnd = clip.start + firstLoopDuration + loopDuration * numCompletedLoops;
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip Last Loop"));
TrimClipWithEditMode(clip, TrimEdge.End, newEnd);
}
public static bool DoubleSpeed(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
if (clip.SupportsSpeedMultiplier())
{
UndoExtensions.RegisterClip(clip, L10n.Tr("Double Clip Speed"));
clip.timeScale = clip.timeScale * 2.0f;
}
}
return true;
}
public static bool HalfSpeed(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
if (clip.SupportsSpeedMultiplier())
{
UndoExtensions.RegisterClip(clip, L10n.Tr("Half Clip Speed"));
clip.timeScale = clip.timeScale * 0.5f;
}
}
return true;
}
public static bool ResetSpeed(IEnumerable<TimelineClip> clips)
{
foreach (var clip in clips)
{
if (clip.timeScale != 1.0)
{
UndoExtensions.RegisterClip(clip, L10n.Tr("Reset Clip Speed"));
clip.timeScale = 1.0;
}
}
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0eeee3cdfa56734abca5c1a4e7989ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,156 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
class Clipboard
{
class ExposedReferenceTable : IExposedPropertyTable
{
Dictionary<PropertyName, Object> m_ReferenceTable = new Dictionary<PropertyName, Object>();
public void SetReferenceValue(PropertyName id, Object value)
{
m_ReferenceTable[id] = value;
}
public Object GetReferenceValue(PropertyName id, out bool idValid)
{
Object reference;
idValid = m_ReferenceTable.TryGetValue(id, out reference);
return reference;
}
public void ClearReferenceValue(PropertyName id)
{
m_ReferenceTable.Remove(id);
}
public void Clear()
{
m_ReferenceTable.Clear();
}
}
public struct ClipboardTrackEntry
{
public TrackAsset item;
public TrackAsset parent;
public List<Object> bindings;
}
static readonly int kListInitialSize = 10;
readonly List<ItemsPerTrack> m_ItemsData = new List<ItemsPerTrack>(kListInitialSize);
readonly List<ClipboardTrackEntry> m_trackData = new List<ClipboardTrackEntry>(kListInitialSize);
TimelineAsset rootTimeline;
public readonly IExposedPropertyTable exposedPropertyTable = new ExposedReferenceTable();
public Clipboard()
{
rootTimeline = CreateTimeline();
EditorApplication.playModeStateChanged += OnPlayModeChanged;
}
public void CopyItems(IEnumerable<ITimelineItem> items)
{
using (new TimelineUndo.DisableUndoGuard(true))
{
var itemsByParent = items.ToLookup(i => i.parentTrack);
foreach (var itemsGroup in itemsByParent)
{
var parent = itemsGroup.Key;
var itemsList = new List<ITimelineItem>();
foreach (var item in itemsGroup)
{
if (item is ClipItem)
itemsList.Add(CopyItem((ClipItem)item));
else if (item is MarkerItem)
itemsList.Add(CopyItem((MarkerItem)item));
}
m_ItemsData.Add(new ItemsPerTrack(parent, itemsList));
}
}
}
ClipItem CopyItem(ClipItem clipItem)
{
var newClip = TimelineHelpers.Clone(clipItem.clip, TimelineWindow.instance.state.editSequence.director, exposedPropertyTable, rootTimeline);
return new ClipItem(newClip);
}
static MarkerItem CopyItem(MarkerItem markerItem)
{
var markerObject = markerItem.marker as Object;
if (markerObject != null)
{
var newMarker = Object.Instantiate(markerObject);
newMarker.name = markerObject.name;
return new MarkerItem((IMarker)newMarker);
}
return null;
}
public void CopyTracks(IEnumerable<TrackAsset> tracks)
{
using (new TimelineUndo.DisableUndoGuard(true))
{
foreach (var track in TrackExtensions.FilterTracks(tracks))
{
var newTrack = track.Duplicate(TimelineEditor.inspectedDirector, TimelineEditor.clipboard.exposedPropertyTable, rootTimeline);
var originalTracks = track.GetFlattenedChildTracks().Append(track);
var newTracks = newTrack.GetFlattenedChildTracks().Append(newTrack);
var toBind = new List<Object>();
// Collect all track bindings to duplicate
var originalIt = originalTracks.GetEnumerator();
var newIt = newTracks.GetEnumerator();
while (originalIt.MoveNext() && newIt.MoveNext())
{
toBind.Add(TimelineEditor.inspectedDirector != null ? TimelineEditor.inspectedDirector.GetGenericBinding(originalIt.Current) : null);
}
m_trackData.Add(new ClipboardTrackEntry { item = newTrack, parent = track.parent as TrackAsset, bindings = toBind });
}
}
}
public IEnumerable<ClipboardTrackEntry> GetTracks()
{
return m_trackData;
}
public IEnumerable<ItemsPerTrack> GetCopiedItems()
{
return m_ItemsData;
}
public void Clear()
{
m_ItemsData.Clear();
m_trackData.Clear();
rootTimeline = CreateTimeline();
((ExposedReferenceTable)exposedPropertyTable).Clear();
}
private void OnPlayModeChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.EnteredPlayMode)
Clear();
}
static TimelineAsset CreateTimeline()
{
var timeline = ScriptableObject.CreateInstance<TimelineAsset>();
timeline.hideFlags |= HideFlags.DontSave;
timeline.name = "Clipboard";
return timeline;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b57629d89799e004182564256307b0cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,62 @@
using System.Collections.Generic;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class ControlPlayableUtility
{
public static bool DetectCycle(
ControlPlayableAsset asset, PlayableDirector director, HashSet<PlayableDirector> set = null)
{
if (director == null || asset == null || !asset.updateDirector)
return false;
if (set == null)
set = new HashSet<PlayableDirector>();
if (set.Contains(director))
return true;
var gameObject = asset.sourceGameObject.Resolve(director);
if (gameObject == null)
return false;
set.Add(director);
foreach (var subDirector in asset.GetComponent<PlayableDirector>(gameObject))
{
foreach (var childAsset in GetPlayableAssets(subDirector))
{
if (DetectCycle(childAsset, subDirector, set))
return true;
}
}
set.Remove(director);
return false;
}
public static IEnumerable<ControlPlayableAsset> GetPlayableAssets(PlayableDirector director)
{
var timeline = director != null ? (director.playableAsset as TimelineAsset) : null;
if (timeline != null)
{
foreach (var t in timeline.GetOutputTracks())
{
var controlTrack = t as ControlTrack;
if (controlTrack != null)
{
foreach (var c in t.GetClips())
{
var asset = c.asset as ControlPlayableAsset;
if (asset != null)
yield return asset;
}
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e801faa3b0dd2478dbe801a2441b679e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
using System;
namespace UnityEditor.Timeline
{
// Tells a custom [[TrackDrawer]] which [[TrackAsset]] it's a drawer for.
sealed class CustomTrackDrawerAttribute : Attribute
{
public Type assetType;
public CustomTrackDrawerAttribute(Type type)
{
assetType = type;
}
}
/// <summary>
/// Attribute that specifies a class as an editor for an extended Timeline type.
/// </summary>
/// <remarks>
/// Use this attribute on a class that extends ClipEditor, TrackEditor, or MarkerEditor to specify either the PlayableAsset, Marker, or TrackAsset derived classes for associated customization.
/// </remarks>
/// <example>
/// <code source="../../DocCodeExamples/TimelineAttributesExamples.cs" region="declare-customTimelineEditorAttr" title="customTimelineEditorAttr"/>
/// </example>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class CustomTimelineEditorAttribute : Attribute
{
/// <summary>
/// The type that that this editor applies to.
/// </summary>
public Type classToEdit { get; private set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="type"> The type that that this editor applies to.</param>
/// <exception cref="ArgumentNullException">Thrown if type is null</exception>
public CustomTimelineEditorAttribute(Type type)
{
if (type == null)
throw new System.ArgumentNullException(nameof(type));
classToEdit = type;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1e957d39ca70834f9212a1289b6a0d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
using System.Text;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
static class DisplayNameHelper
{
static readonly string k_NoAssetDisplayName = L10n.Tr("<No Asset>");
static readonly string k_ReadOnlyDisplayName = L10n.Tr("[Read Only]");
static readonly StringBuilder k_StringBuilder = new StringBuilder();
public static string GetDisplayName(ISequenceState sequence)
{
string displayName = sequence.director != null ? GetDisplayName(sequence.director) : GetDisplayName(sequence.asset);
if (sequence.asset != null && sequence.isReadOnly)
displayName += " " + k_ReadOnlyDisplayName;
return displayName;
}
public static string GetDisplayName(PlayableAsset asset)
{
return asset != null ? asset.name : k_NoAssetDisplayName;
}
public static string GetDisplayName(PlayableDirector director)
{
k_StringBuilder.Length = 0;
k_StringBuilder.Append(GetDisplayName(director.playableAsset));
k_StringBuilder.Append(" (").Append(director.name).Append(')');
return k_StringBuilder.ToString();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7fc2147e42d71644aad0eaf9a3526249
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
using System.IO;
using UnityEditor.VersionControl;
namespace UnityEditor.Timeline
{
static class FileUtility
{
internal static bool IsReadOnly(UnityEngine.Object asset)
{
return IsReadOnlyImpl(asset);
}
#if UNITY_2021_2_OR_NEWER
static bool IsReadOnlyImpl(UnityEngine.Object asset)
{
string assetPath = AssetDatabase.GetAssetPath(asset);
if (string.IsNullOrEmpty(assetPath))
return false;
if (Provider.enabled && VersionControlUtils.IsPathVersioned(assetPath))
{
return !AssetDatabase.CanOpenForEdit(asset, StatusQueryOptions.UseCachedIfPossible);
}
return (uint)(File.GetAttributes(assetPath) & FileAttributes.ReadOnly) > 0U;
}
#else
static bool IsReadOnlyImpl(UnityEngine.Object asset)
{
string assetPath = AssetDatabase.GetAssetPath(asset);
if (Provider.enabled)
{
if (!Provider.isActive)
return false;
Asset vcAsset = Provider.GetAssetByPath(assetPath);
if (Provider.IsOpenForEdit(vcAsset))
return false;
//I can't get any of the Provider checks to work, but here we should check for exclusive checkout issues.
return false;
}
if (!string.IsNullOrEmpty(assetPath))
{
return (File.GetAttributes(assetPath) & FileAttributes.ReadOnly) != 0;
}
return false;
}
#endif
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0303d12aca9843d3bd68adf722d9c9fd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,73 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class FrameRateDisplayUtility
{
private static string[] s_FrameRateLabels;
public static bool GetStandardFromFrameRate(double frameRate, out StandardFrameRates standard)
{
FrameRate frameRateObj = TimeUtility.GetClosestFrameRate(RoundFrameRate(frameRate));
return TimeUtility.ToStandardFrameRate(frameRateObj, out standard);
}
public static double RoundFrameRate(double frameRate)
{
double trunc = Math.Truncate(frameRate * (1 / TimeUtility.kFrameRateRounding)) * TimeUtility.kFrameRateRounding;
return Math.Min(Math.Max(TimelineAsset.EditorSettings.kMinFrameRate, trunc),
TimelineAsset.EditorSettings.kMaxFrameRate);
}
public static string[] GetDefaultFrameRatesLabels(StandardFrameRates option)
{
string[] labels;
if (s_FrameRateLabels == null || !s_FrameRateLabels.Any())
{
var frameRates = (StandardFrameRates[])Enum.GetValues(typeof(StandardFrameRates));
labels = Array.ConvertAll(frameRates, GetLabelForStandardFrameRate);
s_FrameRateLabels = labels;
}
else
{
labels = s_FrameRateLabels;
}
if (!Enum.IsDefined(typeof(StandardFrameRates), option))
{
Array.Resize(ref labels, (int)option + 1);
labels[(int)option] = GetLabelForStandardFrameRate(option);
}
return labels;
}
static string GetLabelForStandardFrameRate(StandardFrameRates option)
{
switch (option)
{
case StandardFrameRates.Fps23_97:
return L10n.Tr("Film NTSC: 23.97 fps");
case StandardFrameRates.Fps24:
return L10n.Tr("Film: 24 fps");
case StandardFrameRates.Fps25:
return L10n.Tr("PAL: 25 fps");
case StandardFrameRates.Fps29_97:
return L10n.Tr("NTSC: 29.97 fps");
case StandardFrameRates.Fps30:
return L10n.Tr("HD: 30 fps");
case StandardFrameRates.Fps50:
return L10n.Tr("Interlaced PAL: 50 fps");
case StandardFrameRates.Fps59_94:
return L10n.Tr("Interlaced NTSC: 59.94 fps");
case StandardFrameRates.Fps60:
return L10n.Tr("Game: 60 fps");
default:
return L10n.Tr("Custom");
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e6372298ccbe45b9a771c50dd0293b6e
timeCreated: 1612896067

View File

@ -0,0 +1,121 @@
using UnityEngine;
namespace UnityEditor.Timeline
{
static class Graphics
{
public static void ShadowLabel(Rect rect, string text, GUIStyle style, Color textColor, Color shadowColor)
{
ShadowLabel(rect, GUIContent.Temp(text), style, textColor, shadowColor);
}
public static void ShadowLabel(Rect rect, GUIContent content, GUIStyle style, Color textColor, Color shadowColor)
{
var shadowRect = rect;
shadowRect.xMin += 2.0f;
shadowRect.yMin += 2.0f;
style.normal.textColor = shadowColor;
style.hover.textColor = shadowColor;
GUI.Label(shadowRect, content, style);
style.normal.textColor = textColor;
style.hover.textColor = textColor;
GUI.Label(rect, content, style);
}
public static void DrawLine(Vector3 p1, Vector3 p2, Color color)
{
var c = Handles.color;
Handles.color = color;
Handles.DrawLine(p1, p2);
Handles.color = c;
}
public static void DrawPolygonAA(Color color, Vector3[] vertices)
{
var prevColor = Handles.color;
Handles.color = color;
Handles.DrawAAConvexPolygon(vertices);
Handles.color = prevColor;
}
public static void DrawDottedLine(Vector3 p1, Vector3 p2, float segmentsLength, Color col)
{
HandleUtility.ApplyWireMaterial();
GL.Begin(GL.LINES);
GL.Color(col);
var length = Vector3.Distance(p1, p2); // ignore z component
var count = Mathf.CeilToInt(length / segmentsLength);
for (var i = 0; i < count; i += 2)
{
GL.Vertex((Vector3.Lerp(p1, p2, i * segmentsLength / length)));
GL.Vertex((Vector3.Lerp(p1, p2, (i + 1) * segmentsLength / length)));
}
GL.End();
}
public static void DrawLineAtTime(WindowState state, double time, Color color, bool dotted = false)
{
var t = state.TimeToPixel(time);
var p0 = new Vector3(t, state.timeAreaRect.yMax);
var p1 = new Vector3(t, state.timeAreaRect.yMax + state.windowHeight - WindowConstants.sliderWidth);
if (dotted)
DrawDottedLine(p0, p1, 4.0f, color);
else
DrawLine(p0, p1, color);
}
public static void DrawTextureRepeated(Rect area, Texture texture)
{
if (texture == null || Event.current.type != EventType.Repaint)
return;
GUI.BeginClip(area);
int w = Mathf.CeilToInt(area.width / texture.width);
int h = Mathf.CeilToInt(area.height / texture.height);
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
GUI.DrawTexture(new Rect(x * texture.width, y * texture.height, texture.width, texture.height), texture);
}
}
GUI.EndClip();
}
public static void DrawShadow(Rect clientRect)
{
var rect = clientRect;
rect.height = WindowConstants.shadowUnderTimelineHeight;
GUI.Box(rect, GUIContent.none, DirectorStyles.Instance.bottomShadow);
}
public static void DrawBackgroundRect(WindowState state, Rect rect, bool subSequenceMode = false)
{
Color c = subSequenceMode ? DirectorStyles.Instance.customSkin.colorSubSequenceBackground : DirectorStyles.Instance.customSkin.colorSequenceBackground;
EditorGUI.DrawRect(rect, c);
if (state.IsEditingAPrefabAsset())
{
c = SceneView.kSceneViewPrefabBackground.Color;
c.a = 0.5f;
EditorGUI.DrawRect(rect, c);
}
}
public static Rect CalculateTextBoxSize(Rect trackRect, GUIStyle font, GUIContent content, float padding)
{
Rect textRect = trackRect;
textRect.width = font.CalcSize(content).x + padding;
textRect.x += (trackRect.width - textRect.width) / 2f;
textRect.height -= 4f;
textRect.y += 2f;
return textRect;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4545bb65ccebf8040ac212d5792979b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Utilities
{
class KeyTraverser
{
float[] m_KeyCache;
int m_DirtyStamp = -1;
int m_LastHash = -1;
readonly TimelineAsset m_Asset;
readonly float m_Epsilon;
int m_LastIndex = -1;
public int lastIndex
{
get { return m_LastIndex; }
}
public static IEnumerable<float> GetClipKeyTimes(TimelineClip clip)
{
if (clip == null || clip.animationClip == null || clip.animationClip.empty)
return new float[0];
return AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip).keyTimes.
Select(k => (float)clip.FromLocalTimeUnbound(k)). // convert to sequence time
Where(k => k >= clip.start && k <= clip.end); // remove non visible keys
}
public static IEnumerable<float> GetTrackKeyTimes(AnimationTrack track)
{
if (track != null)
{
if (track.inClipMode)
return track.clips.Where(c => c.recordable).
SelectMany(x => GetClipKeyTimes(x));
if (track.infiniteClip != null && !track.infiniteClip.empty)
return AnimationClipCurveCache.Instance.GetCurveInfo(track.infiniteClip).keyTimes;
}
return new float[0];
}
static int CalcAnimClipHash(TrackAsset asset)
{
int hash = 0;
if (asset != null)
{
AnimationTrack animTrack = asset as AnimationTrack;
if (animTrack != null)
{
for (var i = 0; i != animTrack.clips.Length; ++i)
{
hash ^= (animTrack.clips[i]).Hash();
}
}
foreach (var subTrack in asset.GetChildTracks())
{
if (subTrack != null)
hash ^= CalcAnimClipHash(subTrack);
}
}
return hash;
}
internal static int CalcAnimClipHash(TimelineAsset asset)
{
int hash = 0;
foreach (var t in asset.GetRootTracks())
{
if (t != null)
hash ^= CalcAnimClipHash(t);
}
return hash;
}
void RebuildKeyCache()
{
m_KeyCache = m_Asset.flattenedTracks.Where(x => (x as AnimationTrack) != null)
.Cast<AnimationTrack>()
.SelectMany(t => GetTrackKeyTimes(t)).
OrderBy(x => x).ToArray();
if (m_KeyCache.Length > 0)
{
float[] unique = new float[m_KeyCache.Length];
unique[0] = m_KeyCache[0];
int index = 0;
for (int i = 1; i < m_KeyCache.Length; i++)
{
if (m_KeyCache[i] - unique[index] > m_Epsilon)
{
index++;
unique[index] = m_KeyCache[i];
}
}
m_KeyCache = unique;
Array.Resize(ref m_KeyCache, index + 1);
}
}
public KeyTraverser(TimelineAsset timeline, float epsilon)
{
m_Asset = timeline;
m_Epsilon = epsilon;
}
void CheckCache(int dirtyStamp)
{
int hash = CalcAnimClipHash(m_Asset);
if (dirtyStamp != m_DirtyStamp || hash != m_LastHash)
{
RebuildKeyCache();
m_DirtyStamp = dirtyStamp;
m_LastHash = hash;
}
}
public float GetNextKey(float key, int dirtyStamp)
{
CheckCache(dirtyStamp);
if (m_KeyCache.Length > 0)
{
if (key < m_KeyCache.Last() - m_Epsilon)
{
if (key > m_KeyCache[0] - m_Epsilon)
{
float t = key + m_Epsilon;
// binary search
int max = m_KeyCache.Length - 1;
int min = 0;
while (max - min > 1)
{
int imid = (min + max) / 2;
if (t > m_KeyCache[imid])
min = imid;
else
max = imid;
}
m_LastIndex = max;
return m_KeyCache[max];
}
m_LastIndex = 0;
return m_KeyCache[0];
}
if (key < m_KeyCache.Last() + m_Epsilon)
{
m_LastIndex = m_KeyCache.Length - 1;
return Mathf.Max(key, m_KeyCache.Last());
}
}
m_LastIndex = -1;
return key;
}
public float GetPrevKey(float key, int dirtyStamp)
{
CheckCache(dirtyStamp);
if (m_KeyCache.Length > 0)
{
if (key > m_KeyCache[0] + m_Epsilon)
{
if (key < m_KeyCache.Last() + m_Epsilon)
{
float t = key - m_Epsilon;
// binary search
int max = m_KeyCache.Length - 1;
int min = 0;
while (max - min > 1)
{
int imid = (min + max) / 2;
if (t < m_KeyCache[imid])
max = imid;
else
min = imid;
}
m_LastIndex = min;
return m_KeyCache[min];
}
m_LastIndex = m_KeyCache.Length - 1;
return m_KeyCache.Last();
}
if (key >= m_KeyCache[0] - m_Epsilon)
{
m_LastIndex = 0;
return Mathf.Min(key, m_KeyCache[0]);
}
}
m_LastIndex = -1;
return key;
}
public int GetKeyCount(int dirtyStamp)
{
CheckCache(dirtyStamp);
return m_KeyCache.Length;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b57f909f22642d469a39e9628535312
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
static class MarkerModifier
{
public static void DeleteMarker(IMarker marker)
{
var trackAsset = marker.parent;
if (trackAsset != null)
{
SelectionManager.Remove(marker);
trackAsset.DeleteMarker(marker);
}
}
public static IEnumerable<IMarker> CloneMarkersToParent(IEnumerable<IMarker> markers, TrackAsset parent)
{
if (!markers.Any()) return Enumerable.Empty<IMarker>();
var clonedMarkers = new List<IMarker>();
foreach (var marker in markers)
clonedMarkers.Add(CloneMarkerToParent(marker, parent));
return clonedMarkers;
}
public static IMarker CloneMarkerToParent(IMarker marker, TrackAsset parent)
{
var markerObject = marker as ScriptableObject;
if (markerObject == null) return null;
var newMarkerObject = Object.Instantiate(markerObject);
AddMarkerToParent(newMarkerObject, parent);
newMarkerObject.name = markerObject.name;
try
{
CustomTimelineEditorCache.GetMarkerEditor((IMarker)newMarkerObject).OnCreate((IMarker)newMarkerObject, marker);
}
catch (Exception e)
{
Debug.LogException(e);
}
return (IMarker)newMarkerObject;
}
static void AddMarkerToParent(ScriptableObject marker, TrackAsset parent)
{
TimelineCreateUtilities.SaveAssetIntoObject(marker, parent);
TimelineUndo.RegisterCreatedObjectUndo(marker, L10n.Tr("Duplicate Marker"));
UndoExtensions.RegisterTrack(parent, L10n.Tr("Duplicate Marker"));
if (parent != null)
{
parent.AddMarker(marker);
((IMarker)marker).Initialize(parent);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7cfaad4e53832d94c9421d2dd1ad82f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Timeline
{
static class ObjectExtension
{
public static bool IsSceneObject(this Object obj)
{
if (obj == null)
return false;
bool isSceneType = obj is GameObject || obj is Component;
if (!isSceneType)
return false;
return !PrefabUtility.IsPartOfPrefabAsset(obj);
}
public static bool IsPrefab(this Object obj)
{
if (obj == null)
return false;
return PrefabUtility.IsPartOfPrefabAsset(obj);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4722a1362908a1843ab03a055c5c3fa0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,195 @@
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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29bf1d4ec1012bc45967ce95b729b8b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Playables;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
readonly struct PreviewedBindings<T> where T : Object
{
readonly IEnumerable<T> m_UniqueBindings;
readonly IReadOnlyDictionary<Object, HashSet<T>> m_BindingsPerObject;
PreviewedBindings(IEnumerable<T> uniqueBindings, IReadOnlyDictionary<Object, HashSet<T>> bindingsPerObject)
{
m_UniqueBindings = uniqueBindings;
m_BindingsPerObject = bindingsPerObject;
}
public IEnumerable<T> GetUniqueBindings() => m_UniqueBindings ?? Enumerable.Empty<T>();
public IEnumerable<T> GetBindingsForObject(Object track)
{
if (m_BindingsPerObject != null && m_BindingsPerObject.TryGetValue(track, out HashSet<T> bindings))
return bindings;
return Enumerable.Empty<T>();
}
public static PreviewedBindings<T> GetPreviewedBindings(IEnumerable<PlayableDirector> directors)
{
var uniqueBindings = new HashSet<T>();
var bindingsPerTrack = new Dictionary<Object, HashSet<T>>();
foreach (PlayableDirector director in directors)
{
if (director.playableAsset == null) continue;
foreach (PlayableBinding output in director.playableAsset.outputs)
{
var binding = director.GetGenericBinding(output.sourceObject) as T;
Add(output.sourceObject, binding, uniqueBindings, bindingsPerTrack);
}
}
return new PreviewedBindings<T>(uniqueBindings, bindingsPerTrack);
}
static void Add(Object obj, T binding, HashSet<T> bindings, Dictionary<Object, HashSet<T>> bindingsPerObject)
{
if (binding == null)
return;
bindings.Add(binding);
if (bindingsPerObject.TryGetValue(obj, out HashSet<T> objectBindings))
objectBindings.Add(binding);
else
bindingsPerObject.Add(obj, new HashSet<T> { binding });
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 907eeaaade2f4a3c9b079743c2bf77ee
timeCreated: 1686150636

View File

@ -0,0 +1,226 @@
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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f3a562675833b4448299e4f627b0cec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,49 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
struct Range
{
public double start;
public double end;
public double length { get { return end - start; } }
public static Range Union(Range lhs, Range rhs)
{
return new Range
{
start = Math.Min(lhs.start, rhs.start),
end = Math.Max(lhs.end, rhs.end)
};
}
public static Range Intersection(Range lhs, Range rhs)
{
var s = Math.Max(lhs.start, rhs.start);
var e = Math.Min(lhs.end, rhs.end);
if (s > e)
{
// No intersection returns a 0-length range from 0 to 0
return new Range();
}
return new Range
{
start = s,
end = e
};
}
public override string ToString()
{
return ToString("F3");
}
public string ToString(string format)
{
return UnityString.Format("({0}, {1})", start.ToString(format), end.ToString(format));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d31dfeaa131921f4eae00783cc48146f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d55400f78d024c05b04dc124bb181d9e
timeCreated: 1605887929

View File

@ -0,0 +1,21 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
struct GUIColorOverride : IDisposable
{
readonly Color m_OldColor;
public GUIColorOverride(Color newColor)
{
m_OldColor = GUI.color;
GUI.color = newColor;
}
public void Dispose()
{
GUI.color = m_OldColor;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44507a833d0ca8a42aaec1c3d752eb5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using System;
using UnityEngine;
namespace UnityEditor
{
struct GUIGroupScope : IDisposable
{
public GUIGroupScope(Rect position)
{
GUI.BeginGroup(position);
}
public void Dispose()
{
GUI.EndGroup();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bee8aba5e8a40446b7098666c5314d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
using System;
using UnityEngine;
namespace UnityEditor
{
struct GUIMixedValueScope : IDisposable
{
readonly bool m_PrevValue;
public GUIMixedValueScope(bool newValue)
{
m_PrevValue = EditorGUI.showMixedValue;
EditorGUI.showMixedValue = newValue;
}
public void Dispose()
{
EditorGUI.showMixedValue = m_PrevValue;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d59cefc45e3c31d4a90563364e7258fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using System;
using UnityEngine;
namespace UnityEditor
{
// Special Clip Scope that only effects painting, and keeps the coordinate system identical
struct GUIViewportScope : IDisposable
{
bool m_open;
public GUIViewportScope(Rect position)
{
m_open = false;
if (Event.current.type == EventType.Repaint || Event.current.type == EventType.Layout)
{
GUI.BeginClip(position, -position.min, Vector2.zero, false);
m_open = true;
}
}
public void Dispose()
{
CloseScope();
}
void CloseScope()
{
if (m_open)
{
GUI.EndClip();
m_open = false;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af84cf39b8fa0654badd9278cbd00d77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
readonly struct HorizontalScope : IDisposable
{
public readonly Rect rect;
public HorizontalScope(GUIContent content, GUIStyle style)
{
rect = EditorGUILayout.BeginHorizontal(content, style);
}
public void Dispose()
{
EditorGUILayout.EndHorizontal();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 75e3737506434a75b1f7901d8883ad47
timeCreated: 1605888588

View File

@ -0,0 +1,20 @@
using System;
namespace UnityEditor.Timeline
{
readonly struct IndentLevelScope : IDisposable
{
readonly int m_PrevValue;
public IndentLevelScope(int newValue)
{
m_PrevValue = EditorGUI.indentLevel;
EditorGUI.indentLevel = newValue;
}
public void Dispose()
{
EditorGUI.indentLevel = m_PrevValue;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e42e3f559ace4a5dbae7d6d3d2e5e7b0
timeCreated: 1605888248

View File

@ -0,0 +1,20 @@
using System;
namespace UnityEditor.Timeline
{
readonly struct LabelWidthScope : IDisposable
{
readonly float m_PrevValue;
public LabelWidthScope(float newValue)
{
m_PrevValue = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = newValue;
}
public void Dispose()
{
EditorGUIUtility.labelWidth = m_PrevValue;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9d095bb54df44c01a27e76a2b060a072
timeCreated: 1605888410

View File

@ -0,0 +1,20 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
readonly struct PropertyScope : IDisposable
{
public readonly GUIContent content;
public PropertyScope(Rect totalPosition, GUIContent label, SerializedProperty property)
{
content = EditorGUI.BeginProperty(totalPosition, label, property);
}
public void Dispose()
{
EditorGUI.EndProperty();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0ccfd55dae664bbcb54c8457acffdbf8
timeCreated: 1605887959

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
namespace UnityEditor.Timeline
{
// Class used for uniquely format names used in the GenericMenu. We can't add duplicate MenuItem in GenericMenu
// so that's why we need to keep information about the text we want to uniquely format.
class SequenceMenuNameFormater
{
Dictionary<int, int> m_UniqueItem = new Dictionary<int, int>();
public string Format(string text)
{
var key = text.GetHashCode();
var index = 0;
if (m_UniqueItem.ContainsKey(key))
{
index = m_UniqueItem[key];
index++;
m_UniqueItem[key] = index;
}
else
{
m_UniqueItem.Add(key, index);
return text;
}
return $"{text}{index}";
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1861286ba69badd439188a65bebf3cda
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
interface IBounds
{
Rect boundingRect { get; }
}
class SpacePartitioner
{
internal class CachedList<T>
{
public static readonly List<T> Instance = new List<T>(1000);
}
struct Entry : IInterval
{
public object item { get; set; }
public long intervalStart { get; set; }
public long intervalEnd { get; set; }
public Rect bounds { get; set; }
private const float kPrecision = 100.0f;
private const float kMaxFloat = (float)long.MaxValue;
private const float kMinFloat = (float)long.MinValue;
static public Int64 FromFloat(float f)
{
if (Single.IsPositiveInfinity(f))
return long.MaxValue;
if (Single.IsNegativeInfinity(f))
return long.MinValue;
f = Mathf.Clamp(f, kMinFloat, kMaxFloat); // prevent overflow of floats
f = Mathf.Clamp(f * kPrecision, kMinFloat, kMaxFloat); // clamp to 'long' range
return (long)(f);
}
}
const EventType k_GuiEventLock = EventType.Repaint;
IntervalTree<Entry> m_Tree = new IntervalTree<Entry>();
List<Entry> m_CacheList = new List<Entry>();
public void Clear()
{
m_Tree.Clear();
}
public void AddBounds(IBounds bounds)
{
AddBounds(bounds, bounds.boundingRect);
}
public void AddBounds(object item, Rect rect)
{
if (item == null)
throw new ArgumentNullException("item");
m_Tree.Add(new Entry()
{
intervalStart = Entry.FromFloat(rect.yMin),
intervalEnd = Entry.FromFloat(rect.yMax),
bounds = rect,
item = item
}
);
}
/// <summary>
/// Get items of type T at a given position
/// </summary>
/// <param name="position"></param>
/// <param name="inClipSpace"></param>
/// <typeparam name="T"></typeparam>
/// <remarks>
/// Uses a (1,1) sized box
/// Use .ToList() or .ToArray() when not enumerating the result immediately
/// </remarks>
/// <returns></returns>
public IEnumerable<T> GetItemsAtPosition<T>(Vector2 position)
{
return GetItemsInArea<T>(new Rect(position.x, position.y, 1, 1));
}
/// <summary>
///
/// </summary>
/// <param name="area"></param>
/// <param name="inClipSpace"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IEnumerable<T> GetItemsInArea<T>(Rect area)
{
m_CacheList.Clear();
m_Tree.IntersectsWithRange(long.MinValue, long.MaxValue, m_CacheList);
var list = CachedList<T>.Instance;
list.Clear();
foreach (var i in m_CacheList)
{
if (i.item is T && i.bounds.Overlaps(area))
list.Add((T)i.item);
}
return list;
}
public void DebugDraw(Color fillColor, Color outlineColor)
{
m_CacheList.Clear();
m_Tree.IntersectsWithRange(long.MinValue, long.MaxValue, m_CacheList);
HandleUtility.ApplyWireMaterial();
foreach (var item in m_CacheList)
{
Handles.DrawSolidRectangleWithOutline(item.bounds, fillColor, outlineColor);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2fa2cf7de51b0d34d9dce3747b72e49d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Experimental;
using UnityEditor.StyleSheets;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class StyleManager
{
static readonly StyleState[] k_StyleStates = { StyleState.any };
static readonly string k_ErrorCannotFindStyle = L10n.Tr("Cannot find style {0} for {1}");
static Dictionary<Type, GUIStyle> s_CustomStyles = new Dictionary<Type, GUIStyle>();
static GUISkin s_CurrentSkin;
public static GUIStyle UssStyleForType(Type type)
{
ClearCacheIfInvalid();
GUIStyle cachedStyle;
if (s_CustomStyles.TryGetValue(type, out cachedStyle))
return cachedStyle;
var style = DirectorStyles.GetGUIStyle(DirectorStyles.markerDefaultStyle);
var customStyleForType = CustomStyleForType(type);
if (customStyleForType != null)
{
if (IsStyleValid(customStyleForType))
style = DirectorStyles.GetGUIStyle(customStyleForType);
else
Debug.LogWarningFormat(k_ErrorCannotFindStyle, customStyleForType, type.Name);
}
s_CustomStyles.Add(type, style);
return style;
}
static string CustomStyleForType(Type type)
{
var attr = (CustomStyleAttribute)type.GetCustomAttributes(typeof(CustomStyleAttribute), true).FirstOrDefault();
return attr != null ? attr.ussStyle : null;
}
static bool IsStyleValid(string ussStyle)
{
return GUISkin.current.FindStyle(ussStyle) != null || EditorResources.styleCatalog.GetStyle(ussStyle, k_StyleStates).IsValid();
}
static void ClearCacheIfInvalid()
{
if (s_CurrentSkin != GUISkin.current)
s_CustomStyles.Clear();
s_CurrentSkin = GUISkin.current;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f31f28cc64c91042976555c016ffd5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
struct StyleNormalColorOverride : IDisposable
{
readonly GUIStyle m_Style;
readonly Color m_OldColor;
public StyleNormalColorOverride(GUIStyle style, Color newColor)
{
m_Style = style;
m_OldColor = style.normal.textColor;
style.normal.textColor = newColor;
}
public void Dispose()
{
m_Style.normal.textColor = m_OldColor;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2bd3ca1fde4b154448ef972b0f9d292e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,85 @@
using System;
using System.Globalization;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
/// <summary>
/// The available display modes for time in the Timeline Editor.
/// </summary>
public enum TimeFormat
{
/// <summary>Displays time values as frames.</summary>
Frames,
/// <summary>Displays time values as timecode (SS:FF) format.</summary>
Timecode,
/// <summary>Displays time values as seconds.</summary>
Seconds
};
static class TimeDisplayUnitExtensions
{
public static TimeArea.TimeFormat ToTimeAreaFormat(this TimeFormat timeDisplayUnit)
{
switch (timeDisplayUnit)
{
case TimeFormat.Frames: return TimeArea.TimeFormat.Frame;
case TimeFormat.Timecode: return TimeArea.TimeFormat.TimeFrame;
case TimeFormat.Seconds: return TimeArea.TimeFormat.None;
}
return TimeArea.TimeFormat.Frame;
}
public static string ToTimeString(this TimeFormat timeFormat, double time, double frameRate, string format = "f2")
{
switch (timeFormat)
{
case TimeFormat.Frames: return TimeUtility.TimeAsFrames(time, frameRate, format);
case TimeFormat.Timecode: return TimeUtility.TimeAsTimeCode(time, frameRate, format);
case TimeFormat.Seconds: return time.ToString(format, (IFormatProvider)CultureInfo.InvariantCulture.NumberFormat);
}
return time.ToString(format);
}
public static string ToTimeStringWithDelta(this TimeFormat timeFormat, double time, double frameRate, double delta, string format = "f2")
{
const double epsilon = 1e-7;
var result = ToTimeString(timeFormat, time, frameRate, format);
if (delta > epsilon || delta < -epsilon)
{
var sign = ((delta >= 0) ? "+" : "-");
var deltaStr = ToTimeString(timeFormat, Math.Abs(delta), frameRate, format);
return $"{result} ({sign}{deltaStr})";
}
return result;
}
public static double FromTimeString(this TimeFormat timeFormat, string timeString, double frameRate, double defaultValue)
{
double time = defaultValue;
switch (timeFormat)
{
case TimeFormat.Frames:
if (!double.TryParse(timeString, NumberStyles.Any, CultureInfo.InvariantCulture, out time))
return defaultValue;
time = TimeUtility.FromFrames(time, frameRate);
break;
case TimeFormat.Seconds:
time = TimeUtility.ParseTimeSeconds(timeString, frameRate, defaultValue);
break;
case TimeFormat.Timecode:
time = TimeUtility.ParseTimeCode(timeString, frameRate, defaultValue);
break;
default:
time = defaultValue;
break;
}
return time;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 25eff19ce9634557bc1b23c644c46692
timeCreated: 1602253301

View File

@ -0,0 +1,62 @@
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class TimeReferenceUtility
{
static WindowState state { get { return TimelineWindow.instance.state; } }
public static float PixelToTime(Vector2 mousePos)
{
return PixelToTime(mousePos.x);
}
public static float PixelToTime(float pixelX)
{
return state.PixelToTime(pixelX);
}
public static double GetSnappedTimeAtMousePosition(Vector2 mousePos)
{
return state.GetSnappedTimeAtMousePosition(mousePos);
}
public static double SnapToFrameIfRequired(double currentTime)
{
return TimelinePreferences.instance.snapToFrame ? SnapToFrame(currentTime) : currentTime;
}
public static double SnapToFrame(double time)
{
if (state.timeReferenceMode == TimeReferenceMode.Global)
{
time = state.editSequence.ToGlobalTime(time);
time = TimeUtility.RoundToFrame(time, state.referenceSequence.frameRate);
return state.editSequence.ToLocalTime(time);
}
return TimeUtility.RoundToFrame(time, state.referenceSequence.frameRate);
}
public static string ToTimeString(double time, string format = "F2")
{
if (state.timeReferenceMode == TimeReferenceMode.Global)
time = state.editSequence.ToGlobalTime(time);
return state.timeFormat.ToTimeString(time, state.referenceSequence.frameRate, format);
}
public static double FromTimeString(string timeString)
{
double newTime = state.timeFormat.FromTimeString(timeString, state.referenceSequence.frameRate, -1);
if (newTime >= 0.0)
{
return state.timeReferenceMode == TimeReferenceMode.Global ?
state.editSequence.ToLocalTime(newTime) : newTime;
}
return state.editSequence.time;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6bb32665bcc91b41a7177fd6af08ad6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,461 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.IMGUI.Controls;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class KeyboardNavigation
{
public static void FrameTrackHeader(TreeViewItem treeItem = null)
{
if (TrackHeadActive())
treeItem = treeItem ?? SelectionManager.SelectedTrackGUI().Last();
else
{
var item = GetVisibleSelectedItems().LastOrDefault();
treeItem = TimelineWindow.instance.allTracks.FirstOrDefault(
x => item != null && x.track == item.parentTrack);
}
if (treeItem != null)
TimelineWindow.instance.treeView.FrameItem(treeItem);
}
public static bool TrackHeadActive()
{
return SelectionManager.SelectedTracks().Any(x => x.IsVisibleInHierarchy()) && !ClipAreaActive();
}
public static bool ClipAreaActive()
{
return GetVisibleSelectedItems().Any();
}
public static IEnumerable<ITimelineItem> GetVisibleSelectedItems()
{
return SelectionManager.SelectedItems().Where(x => x.parentTrack.IsVisibleInHierarchy());
}
public static IEnumerable<TimelineTrackBaseGUI> GetVisibleTracks()
{
return TimelineWindow.instance.allTracks.Where(x => x.track.IsVisibleInHierarchy());
}
static TrackAsset PreviousTrack(this TrackAsset track)
{
var uiOrderTracks = GetVisibleTracks().Select(t => t.track).ToList();
var selIdx = uiOrderTracks.IndexOf(track);
return selIdx > 0 ? uiOrderTracks[selIdx - 1] : null;
}
static TrackAsset NextTrack(this TrackAsset track)
{
var uiOrderTracks = GetVisibleTracks().Select(t => t.track).ToList();
var selIdx = uiOrderTracks.IndexOf(track);
return selIdx < uiOrderTracks.Count - 1 && selIdx != -1 ? uiOrderTracks[selIdx + 1] : null;
}
static ITimelineItem PreviousItem(this ITimelineItem item, bool clipOnly)
{
var items = item.parentTrack.GetItems().ToArray();
if (clipOnly)
{
items = items.Where(x => x is ClipItem).ToArray();
}
else
{
items = items.Where(x => x is MarkerItem).ToArray();
}
var idx = Array.IndexOf(items, item);
return idx > 0 ? items[idx - 1] : null;
}
static ITimelineItem NextItem(this ITimelineItem item, bool clipOnly)
{
var items = item.parentTrack.GetItems().ToArray();
if (clipOnly)
{
items = items.Where(x => x is ClipItem).ToArray();
}
else
{
items = items.Where(x => x is MarkerItem).ToArray();
}
var idx = Array.IndexOf(items, item);
return idx < items.Length - 1 ? items[idx + 1] : null;
}
static bool FilterItems(ref List<ITimelineItem> items)
{
var clipOnly = false;
if (items.Any(x => x is ClipItem))
{
items = items.Where(x => x is ClipItem).ToList();
clipOnly = true;
}
return clipOnly;
}
static ITimelineItem GetClosestItem(TrackAsset track, ITimelineItem refItem)
{
var start = refItem.start;
var end = refItem.end;
var items = track.GetItems().ToList();
if (refItem is ClipItem)
{
items = items.Where(x => x is ClipItem).ToList();
}
else
{
items = items.Where(x => x is MarkerItem).ToList();
}
if (!items.Any())
return null;
ITimelineItem ret = null;
var scoreToBeat = double.NegativeInfinity;
foreach (var item in items)
{
// test for overlap
var low = Math.Max(item.start, start);
var high = Math.Min(item.end, end);
if (low <= high)
{
var score = high - low;
if (score >= scoreToBeat)
{
scoreToBeat = score;
ret = item;
}
}
}
return ret;
}
public static bool FocusFirstVisibleItem(IEnumerable<TrackAsset> focusTracks = null)
{
var timeRange = TimelineEditor.visibleTimeRange;
var tracks = focusTracks ?? TimelineWindow.instance.treeView.visibleTracks.Where(x => x.IsVisibleInHierarchy() && x.GetItems().Any());
var items = tracks.SelectMany(t => t.GetItems().OfType<ClipItem>().Where(x => x.end >= timeRange.x && x.end <= timeRange.y ||
x.start >= timeRange.x && x.start <= timeRange.y)).ToList();
var itemFullyInView = items.Where(x => x.end >= timeRange.x && x.end <= timeRange.y &&
x.start >= timeRange.x && x.start <= timeRange.y);
var itemToSelect = itemFullyInView.FirstOrDefault() ?? items.FirstOrDefault();
if (itemToSelect != null && !itemToSelect.parentTrack.lockedInHierarchy)
{
SelectionManager.SelectOnly(itemToSelect);
return true;
}
return false;
}
public static bool NavigateLeft(IEnumerable<TrackAsset> tracks)
{
if (!TrackHeadActive())
return false;
if (TryCollapse(tracks))
return true;
return SelectAndShowParentTrack(tracks.LastOrDefault());
}
/// <summary>
/// Tries to collapse any track from a list of tracks
/// </summary>
/// <param name="tracks"></param>
/// <returns>true if any were collapsed, false otherwise</returns>
public static bool TryCollapse(IEnumerable<TrackAsset> tracks)
{
var didCollapse = false;
foreach (TrackAsset track in tracks)
{
if (!track.GetChildTracks().Any())
continue;
if (!track.IsCollapsed())
{
didCollapse = true;
track.SetCollapsed(true);
}
}
if (didCollapse)
{
TimelineEditor.window.treeView.Reload();
return true;
}
return false;
}
public static bool ToggleCollapseGroup(IEnumerable<TrackAsset> tracks)
{
if (!TrackHeadActive())
return false;
var didChange = false;
foreach (TrackAsset track in tracks)
{
if (!track.GetChildTracks().Any())
continue;
track.SetCollapsed(!track.IsCollapsed());
didChange = true;
}
if (didChange)
TimelineEditor.window.treeView.Reload();
return didChange;
}
static bool SelectAndShowParentTrack(TrackAsset track)
{
TrackAsset parent = track != null ? track.parent as TrackAsset : null;
if (parent)
{
SelectionManager.SelectOnly(parent);
FrameTrackHeader(GetVisibleTracks().FirstOrDefault(x => x.track == parent));
return true;
}
return false;
}
public static bool SelectLeftItem(bool shift = false)
{
if (ClipAreaActive())
{
var items = SelectionManager.SelectedItems().ToList();
var clipOnly = FilterItems(ref items);
var item = items.Last();
var prev = item.PreviousItem(clipOnly);
if (prev != null && !prev.parentTrack.lockedInHierarchy)
{
if (shift)
{
if (SelectionManager.Contains(prev))
SelectionManager.Remove(item);
SelectionManager.Add(prev);
}
else
SelectionManager.SelectOnly(prev);
TimelineHelpers.FrameItems(new[] { prev });
}
else if (item != null && !shift && item.parentTrack != TimelineEditor.inspectedAsset.markerTrack)
SelectionManager.SelectOnly(item.parentTrack);
return true;
}
return false;
}
public static bool SelectRightItem(bool shift = false)
{
if (ClipAreaActive())
{
var items = SelectionManager.SelectedItems().ToList();
var clipOnly = FilterItems(ref items);
var item = items.Last();
var next = item.NextItem(clipOnly);
if (next != null && !next.parentTrack.lockedInHierarchy)
{
if (shift)
{
if (SelectionManager.Contains(next))
SelectionManager.Remove(item);
SelectionManager.Add(next);
}
else
SelectionManager.SelectOnly(next);
TimelineHelpers.FrameItems(new[] { next });
return true;
}
}
return false;
}
public static bool NavigateRight(IEnumerable<TrackAsset> tracks)
{
if (!TrackHeadActive())
return false;
if (TryExpand(tracks))
return true;
return TrySelectFirstChild(tracks);
}
/// <summary>
/// Tries to expand from a list of tracks
/// </summary>
/// <param name="tracks"></param>
/// <returns>true if any expanded, false otherwise</returns>
public static bool TryExpand(IEnumerable<TrackAsset> tracks)
{
var didExpand = false;
foreach (TrackAsset track in tracks)
{
if (!track.GetChildTracks().Any())
continue;
if (track.IsCollapsed())
{
didExpand = true;
track.SetCollapsed(false);
}
}
if (didExpand)
{
TimelineEditor.window.treeView.Reload();
}
return didExpand;
}
/// <summary>
/// Tries to select the first clip from a list of tracks
/// </summary>
/// <param name="tracks"></param>
/// <returns>true if any expanded, false otherwise</returns>
public static bool TrySelectFirstChild(IEnumerable<TrackAsset> tracks)
{
foreach (var track in tracks)
{
//Try to navigate in group tracks first
if (track is GroupTrack)
{
if (track.GetChildTracks().Any())
{
SelectionManager.SelectOnly(track.GetChildTracks().First());
return true;
}
//Group tracks should not halt navigation
continue;
}
//if track is locked or has no clips, do nothing
if (track.lockedInHierarchy || !track.clips.Any())
continue;
var firstClip = track.clips.First();
SelectionManager.SelectOnly(firstClip);
TimelineHelpers.FrameItems(new ITimelineItem[] { firstClip.ToItem() });
return true;
}
return false;
}
public static bool SelectUpTrack(bool shift = false)
{
if (TrackHeadActive())
{
var prevTrack = PreviousTrack(SelectionManager.SelectedTracks().Last());
if (prevTrack != null)
{
if (shift)
{
if (SelectionManager.Contains(prevTrack))
SelectionManager.Remove(SelectionManager.SelectedTracks().Last());
SelectionManager.Add(prevTrack);
}
else
SelectionManager.SelectOnly(prevTrack);
FrameTrackHeader(GetVisibleTracks().First(x => x.track == prevTrack));
}
return true;
}
return false;
}
public static bool SelectUpItem()
{
if (ClipAreaActive())
{
var refItem = SelectionManager.SelectedItems().Last();
var prevTrack = refItem.parentTrack.PreviousTrack();
while (prevTrack != null)
{
var selectionItem = GetClosestItem(prevTrack, refItem);
if (selectionItem == null || prevTrack.lockedInHierarchy)
{
prevTrack = prevTrack.PreviousTrack();
continue;
}
SelectionManager.SelectOnly(selectionItem);
TimelineHelpers.FrameItems(new[] { selectionItem });
FrameTrackHeader(GetVisibleTracks().First(x => x.track == selectionItem.parentTrack));
break;
}
return true;
}
return false;
}
public static bool SelectDownTrack(bool shift = false)
{
if (TrackHeadActive())
{
var nextTrack = SelectionManager.SelectedTracks().Last().NextTrack();
if (nextTrack != null)
{
if (shift)
{
if (SelectionManager.Contains(nextTrack))
SelectionManager.Remove(SelectionManager.SelectedTracks().Last());
SelectionManager.Add(nextTrack);
}
else
SelectionManager.SelectOnly(nextTrack);
FrameTrackHeader(GetVisibleTracks().First(x => x.track == nextTrack));
}
return true;
}
return false;
}
public static bool SelectDownItem()
{
if (ClipAreaActive())
{
var refItem = SelectionManager.SelectedItems().Last();
var nextTrack = refItem.parentTrack.NextTrack();
while (nextTrack != null)
{
var selectionItem = GetClosestItem(nextTrack, refItem);
if (selectionItem == null || nextTrack.lockedInHierarchy)
{
nextTrack = nextTrack.NextTrack();
continue;
}
SelectionManager.SelectOnly(selectionItem);
TimelineHelpers.FrameItems(new[] { selectionItem });
FrameTrackHeader(GetVisibleTracks().First(x => x.track == selectionItem.parentTrack));
break;
}
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9be6112c2b1c3ae44927680ba7b36e10
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.Timeline;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
static class TrackModifier
{
public static bool DeleteTrack(TimelineAsset timeline, TrackAsset track)
{
if (TimelineEditor.inspectedDirector != null)
{
TimelineUndo.PushUndo(TimelineEditor.inspectedDirector, L10n.Tr("Delete Track"));
TimelineEditor.inspectedDirector.ClearGenericBinding(track);
}
return timeline.DeleteTrack(track);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 411b7c7ffc0960249b35a2a247b66ff7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class TrackResourceCache
{
private static Dictionary<System.Type, GUIContent> s_TrackIconCache = new Dictionary<Type, GUIContent>(10);
private static Dictionary<System.Type, Color> s_TrackColorCache = new Dictionary<Type, Color>(10);
public static GUIContent s_DefaultIcon = EditorGUIUtility.IconContent("UnityEngine/ScriptableObject Icon");
public static GUIContent GetTrackIcon(TrackAsset track)
{
if (track == null)
return s_DefaultIcon;
GUIContent content = null;
if (!s_TrackIconCache.TryGetValue(track.GetType(), out content))
{
content = FindTrackIcon(track);
s_TrackIconCache[track.GetType()] = content;
}
return content;
}
public static Texture2D GetTrackIconForType(System.Type trackType)
{
if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
return null;
GUIContent content;
if (!s_TrackIconCache.TryGetValue(trackType, out content) || content.image == null)
return s_DefaultIcon.image as Texture2D;
return content.image as Texture2D;
}
public static Color GetTrackColor(TrackAsset track)
{
if (track == null)
return Color.white;
// Try to ensure DirectorStyles is initialized first
// Note: GUISkin.current must exist to be able do so
if (!DirectorStyles.IsInitialized && GUISkin.current != null)
DirectorStyles.ReloadStylesIfNeeded();
Color color;
if (!s_TrackColorCache.TryGetValue(track.GetType(), out color))
{
var attr = track.GetType().GetCustomAttributes(typeof(TrackColorAttribute), true);
if (attr.Length > 0)
{
color = ((TrackColorAttribute)attr[0]).color;
}
else
{
// case 1141958
// There was an error initializing DirectorStyles
if (!DirectorStyles.IsInitialized)
return Color.white;
color = DirectorStyles.Instance.customSkin.colorDefaultTrackDrawer;
}
s_TrackColorCache[track.GetType()] = color;
}
return color;
}
public static void ClearTrackIconCache()
{
s_TrackIconCache.Clear();
}
public static void SetTrackIcon<T>(GUIContent content) where T : TrackAsset
{
s_TrackIconCache[typeof(T)] = content;
}
public static void ClearTrackColorCache()
{
s_TrackColorCache.Clear();
}
public static void SetTrackColor<T>(Color c) where T : TrackAsset
{
s_TrackColorCache[typeof(T)] = c;
}
private static GUIContent FindTrackIcon(TrackAsset track)
{
// backwards compatible -- try to load from Gizmos folder
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/Gizmos/" + track.GetType().Name + ".png");
if (texture != null)
return new GUIContent(texture);
// try to load based on the binding type
var binding = track.outputs.FirstOrDefault();
if (binding.outputTargetType != null)
{
// Type calls don't properly handle monobehaviours, because an instance is required to
// get the monoscript icons
if (typeof(MonoBehaviour).IsAssignableFrom(binding.outputTargetType))
{
texture = null;
var scripts = UnityEngine.Resources.FindObjectsOfTypeAll<MonoScript>();
foreach (var script in scripts)
{
if (script.GetClass() == binding.outputTargetType)
{
texture = AssetPreview.GetMiniThumbnail(script);
break;
}
}
}
else
{
texture = EditorGUIUtility.FindTexture(binding.outputTargetType);
}
if (texture != null)
return new GUIContent(texture);
}
// default to the scriptable object icon
return s_DefaultIcon;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 63f2caa33e79582448112b2e286d576d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,373 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using Component = UnityEngine.Component;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
static class TypeUtility
{
private static List<Type> s_AllTrackTypes;
private static List<Type> s_AllClipTypes;
private static List<Type> s_MarkerTypes;
private static Dictionary<Type, Type[]> s_TrackTypeToVisibleClipType = new Dictionary<Type, Type[]>();
private static Dictionary<Type, Type[]> s_TrackTypeToAllClipType = new Dictionary<Type, Type[]>();
private static Dictionary<Type, TrackBindingTypeAttribute> s_TrackToBindingCache = new Dictionary<Type, TrackBindingTypeAttribute>();
public static bool IsConcretePlayableAsset(Type t)
{
return typeof(IPlayableAsset).IsAssignableFrom(t)
&& IsConcreteAsset(t);
}
private static bool IsConcreteAsset(Type t)
{
return typeof(ScriptableObject).IsAssignableFrom(t)
&& !t.IsAbstract
&& !t.IsGenericType
&& !t.IsInterface
&& !typeof(TrackAsset).IsAssignableFrom(t)
&& !typeof(TimelineAsset).IsAssignableFrom(t);
}
class TimelineTypeComparer : IComparer<Type>
{
public static readonly TimelineTypeComparer Instance = new TimelineTypeComparer();
public int Compare(Type x, Type y)
{
if (ReferenceEquals(x, y))
return 0;
if (ReferenceEquals(null, y))
return 1;
if (ReferenceEquals(null, x))
return -1;
if (IsBuiltIn(x) && !IsBuiltIn(y))
return -1;
if (!IsBuiltIn(x) && IsBuiltIn(y))
return 1;
return string.Compare(x.AssemblyQualifiedName, y.AssemblyQualifiedName, StringComparison.InvariantCultureIgnoreCase);
}
TimelineTypeComparer() { }
}
/// <summary>
/// List of all PlayableAssets
/// </summary>
public static IEnumerable<Type> AllClipTypes()
{
if (s_AllClipTypes == null)
{
s_AllClipTypes = TypeCache.GetTypesDerivedFrom<IPlayableAsset>().
Where(t => IsConcreteAsset(t)).
ToList();
s_AllClipTypes.Sort(TimelineTypeComparer.Instance);
}
return s_AllClipTypes;
}
public static IEnumerable<Type> AllTrackTypes()
{
if (s_AllTrackTypes == null)
{
s_AllTrackTypes = TypeCache.GetTypesDerivedFrom<TrackAsset>()
.Where(x => !x.IsAbstract)
.ToList();
s_AllTrackTypes.Sort(TimelineTypeComparer.Instance);
}
return s_AllTrackTypes;
}
public static IEnumerable<Type> GetVisiblePlayableAssetsHandledByTrack(Type trackType)
{
if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
return Enumerable.Empty<Type>();
Type[] types;
if (s_TrackTypeToVisibleClipType.TryGetValue(trackType, out types))
{
return types;
}
// special case -- the playable track handles all types not handled by other tracks
if (trackType == typeof(PlayableTrack))
{
types = GetUnhandledClipTypes().ToArray();
s_TrackTypeToVisibleClipType[trackType] = types;
return types;
}
var attributes = trackType.GetCustomAttributes(typeof(TrackClipTypeAttribute), true);
var baseClasses = attributes.
OfType<TrackClipTypeAttribute>().
Where(t => t.allowAutoCreate).
Select(a => a.inspectedType);
types = AllClipTypes().Where(t => baseClasses.Any(x => x.IsAssignableFrom(t))).ToArray();
s_TrackTypeToVisibleClipType[trackType] = types;
return types;
}
public static IEnumerable<Type> GetPlayableAssetsHandledByTrack(Type trackType)
{
if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
return Enumerable.Empty<Type>();
Type[] types;
if (s_TrackTypeToAllClipType.TryGetValue(trackType, out types))
{
return types;
}
// special case -- the playable track handles all types not handled by other tracks
if (trackType == typeof(PlayableTrack))
{
types = GetUnhandledClipTypes().ToArray();
s_TrackTypeToAllClipType[trackType] = types;
return types;
}
var attributes = trackType.GetCustomAttributes(typeof(TrackClipTypeAttribute), true);
var baseClasses = attributes.
OfType<TrackClipTypeAttribute>().
Select(a => a.inspectedType);
types = AllClipTypes().Where(t => baseClasses.Any(x => x.IsAssignableFrom(t))).ToArray();
s_TrackTypeToAllClipType[trackType] = types;
return types;
}
/// <summary>
/// Returns the binding attribute attrached to the track
/// </summary>
public static TrackBindingTypeAttribute GetTrackBindingAttribute(Type trackType)
{
if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
return null;
TrackBindingTypeAttribute attribute = null;
if (!s_TrackToBindingCache.TryGetValue(trackType, out attribute))
{
attribute = (TrackBindingTypeAttribute)Attribute.GetCustomAttribute(trackType, typeof(TrackBindingTypeAttribute));
s_TrackToBindingCache.Add(trackType, attribute);
}
return attribute;
}
/// <summary>
/// True if the given track has a clip type that handles the given object
/// </summary>
public static bool TrackHasClipForObject(Type trackType, Object obj)
{
return GetPlayableAssetsHandledByTrack(trackType)
.Any(c => ObjectReferenceField.FindObjectReferences(c).Any(o => o.IsAssignable(obj)));
}
/// <summary>
/// Get the list of markers that have fields for the object
/// </summary>
public static IEnumerable<Type> MarkerTypesWithFieldForObject(Object obj)
{
return GetAllMarkerTypes().Where(
c => ObjectReferenceField.FindObjectReferences(c).Any(o => o.IsAssignable(obj))
);
}
/// <summary>
/// Get the list of tracks that can handle this object as clips
/// </summary>
public static IEnumerable<Type> GetTrackTypesForObject(Object obj)
{
if (obj == null)
return Enumerable.Empty<Type>();
return AllTrackTypes().Where(t => TrackHasClipForObject(t, obj));
}
/// <summary>
/// Given a trackType and an object, does the binding type match
/// Takes into account whether creating a missing component is permitted
/// </summary>
public static bool IsTrackCreatableFromObject(Object obj, Type trackType)
{
if (obj == null || obj.IsPrefab())
return false;
var attribute = GetTrackBindingAttribute(trackType);
if (attribute == null || attribute.type == null)
return false;
if (attribute.type.IsAssignableFrom(obj.GetType()))
return true;
var gameObject = obj as GameObject;
if (gameObject != null && typeof(Component).IsAssignableFrom(attribute.type))
{
return gameObject.GetComponent(attribute.type) != null ||
(attribute.flags & TrackBindingFlags.AllowCreateComponent) != 0;
}
return false;
}
/// <summary>
/// Given an object, get the list of track that are creatable from it. Takes
/// binding flags into account
/// </summary>
public static IEnumerable<Type> GetTracksCreatableFromObject(Object obj)
{
if (obj == null)
return Enumerable.Empty<Type>();
return AllTrackTypes().Where(t => !IsHiddenInMenu(t) && IsTrackCreatableFromObject(obj, t));
}
/// <summary>
/// Get the list of playable assets that can handle an object for a particular track
/// </summary>
/// <param name="trackType">The type of the track</param>
/// <param name="obj">The object to handle</param>
/// <returns></returns>
public static IEnumerable<Type> GetAssetTypesForObject(Type trackType, Object obj)
{
if (obj == null)
return Enumerable.Empty<Type>();
return GetPlayableAssetsHandledByTrack(trackType).Where(
c => ObjectReferenceField.FindObjectReferences(c).Any(o => o.IsAssignable(obj))
);
}
// get the track types for a track from it's attributes
private static IEnumerable<Type> GetTrackClipTypesFromAttributes(Type trackType)
{
if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
return Enumerable.Empty<Type>();
var attributes = trackType.GetCustomAttributes(typeof(TrackClipTypeAttribute), true);
var baseClasses = attributes.
OfType<TrackClipTypeAttribute>().
Select(a => a.inspectedType);
return AllClipTypes().Where(t => baseClasses.Any(x => x.IsAssignableFrom(t)));
}
// find the playable asset types that are unhandled
private static IEnumerable<Type> GetUnhandledClipTypes()
{
var typesHandledByTrack = AllTrackTypes().SelectMany(t => GetTrackClipTypesFromAttributes(t));
// exclude anything in the timeline assembly, handled by tracks, has a hide in menu attribute
// or is explicity ignored
return AllClipTypes()
.Except(typesHandledByTrack)
.Where(t => !TypeUtility.IsBuiltIn(t)) // exclude built-in
.Where(t => !typeof(TrackAsset).IsAssignableFrom(t)) // exclude track types (they are playable assets)
.Where(t => !t.IsDefined(typeof(HideInMenuAttribute), false) && !t.IsDefined(typeof(IgnoreOnPlayableTrackAttribute), true))
.Distinct();
}
public static IEnumerable<Type> GetAllMarkerTypes()
{
if (s_MarkerTypes == null)
{
s_MarkerTypes = TypeCache.GetTypesDerivedFrom<IMarker>()
.Where(x =>
typeof(ScriptableObject).IsAssignableFrom(x)
&& !x.IsAbstract
&& !x.IsGenericType
&& !x.IsInterface)
.ToList();
s_MarkerTypes.Sort(TimelineTypeComparer.Instance);
}
return s_MarkerTypes;
}
public static IEnumerable<Type> GetUserMarkerTypes()
{
return GetAllMarkerTypes().Where(x => !IsBuiltIn(x) && !IsHiddenInMenu(x));
}
public static IEnumerable<Type> GetBuiltInMarkerTypes()
{
return GetAllMarkerTypes().Where(IsBuiltIn);
}
public static bool DoesTrackSupportMarkerType(TrackAsset track, Type type)
{
if (track.supportsNotifications)
{
return true;
}
return !typeof(INotification).IsAssignableFrom(type);
}
internal static string GetDisplayName(Type t)
{
var displayName = ObjectNames.NicifyVariableName(t.Name);
var attr = Attribute.GetCustomAttribute(t, typeof(DisplayNameAttribute), false) as DisplayNameAttribute;
if (attr != null)
displayName = attr.DisplayName;
return L10n.Tr(displayName);
}
public static bool IsHiddenInMenu(Type type)
{
var attr = type.GetCustomAttributes(typeof(HideInMenuAttribute), false);
return attr.Length > 0;
}
public struct ObjectReference
{
public Type type;
public bool isSceneReference;
}
public static IEnumerable<ObjectReference> ObjectReferencesForType(Type type)
{
var objectReferences = ObjectReferenceField.FindObjectReferences(type);
var uniqueTypes = objectReferences.Select(objRef => objRef.type).Distinct();
foreach (var refType in uniqueTypes)
{
var isSceneReference = objectReferences.Any(objRef => objRef.type == refType && objRef.isSceneReference);
yield return new ObjectReference { type = refType, isSceneReference = isSceneReference };
}
}
/// <summary>
/// Checks whether a type has an overridden method with a specific name. This method also checks overridden members in parent classes.
/// </summary>
public static bool HasOverrideMethod(System.Type t, string name)
{
const MethodAttributes mask = MethodAttributes.Virtual | MethodAttributes.NewSlot;
const MethodAttributes expectedResult = MethodAttributes.Virtual;
var method = t.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
return method != null && (method.Attributes & mask) == expectedResult;
}
/// <summary>
/// Returns whether the given type resides in the timeline assembly
/// </summary>
public static bool IsBuiltIn(System.Type t)
{
return t != null && t.Assembly.Equals(typeof(TimelineAsset).Assembly);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c1821c1816c6fa44967b8ecb79ea7e4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: