|
|
using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using UnityEditor.MemoryProfiler;using UnityEditor.SceneManagement;using UnityEngine;using UnityEngine.Playables;using UnityEngine.Timeline;using Object = UnityEngine.Object;
namespace UnityEditor.Timeline{ static class TimelineHelpers { static List<Type> s_SubClassesOfTrackDrawer;
// check whether the exposed reference is explicitly named
static bool IsExposedReferenceExplicitlyNamed(string name) { if (string.IsNullOrEmpty(name)) return false;
GUID guid; return !GUID.TryParse(name, out guid); }
static string GenerateExposedReferenceName() { return UnityEditor.GUID.Generate().ToString(); }
public static void CloneExposedReferences(ScriptableObject clone, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable) { var cloneObject = new SerializedObject(clone); SerializedProperty prop = cloneObject.GetIterator(); while (prop.Next(true)) { if (prop.propertyType == SerializedPropertyType.ExposedReference) { var exposedNameProp = prop.FindPropertyRelative("exposedName"); var sourceKey = exposedNameProp.stringValue; var destKey = sourceKey;
if (!IsExposedReferenceExplicitlyNamed(sourceKey)) destKey = GenerateExposedReferenceName();
exposedNameProp.stringValue = destKey;
var requiresCopy = sourceTable != destTable || sourceKey != destKey; if (requiresCopy && sourceTable != null && destTable != null) { var valid = false; var target = sourceTable.GetReferenceValue(sourceKey, out valid); if (valid && target != null) { var existing = destTable.GetReferenceValue(destKey, out valid); if (!valid || existing != target) { var destTableObj = destTable as UnityEngine.Object; if (destTableObj != null) TimelineUndo.PushUndo(destTableObj, "Create Clip"); destTable.SetReferenceValue(destKey, target); } } } } } cloneObject.ApplyModifiedPropertiesWithoutUndo(); }
public static ScriptableObject CloneReferencedPlayableAsset(ScriptableObject original, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, Object newOwner) { var clone = Object.Instantiate(original); SaveCloneToAsset(clone, newOwner); if (clone == null || (clone as IPlayableAsset) == null) { throw new InvalidCastException("could not cast instantiated object into IPlayableAsset"); } CloneExposedReferences(clone, sourceTable, destTable); TimelineUndo.RegisterCreatedObjectUndo(clone, "Create clip");
return clone; }
static void SaveCloneToAsset(Object clone, Object newOwner) { if (newOwner == null) return;
var containerPath = AssetDatabase.GetAssetPath(newOwner); var containerAsset = AssetDatabase.LoadAssetAtPath<Object>(containerPath); if (containerAsset != null) { TimelineCreateUtilities.SaveAssetIntoObject(clone, containerAsset); EditorUtility.SetDirty(containerAsset); } }
static AnimationClip CloneAnimationClip(AnimationClip clip, Object owner) { if (clip == null) return null;
var newClip = Object.Instantiate(clip); newClip.name = AnimationTrackRecorder.GetUniqueRecordedClipName(owner, clip.name);
SaveAnimClipIntoObject(newClip, owner); TimelineUndo.RegisterCreatedObjectUndo(newClip, "Create clip");
return newClip; }
public static TimelineClip Clone(TimelineClip clip, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, double time, PlayableAsset newOwner = null) { if (newOwner == null) newOwner = clip.parentTrack;
TimelineClip newClip = DuplicateClip(clip, sourceTable, destTable, newOwner); newClip.start = time; var track = newClip.parentTrack; track.SortClips(); TrackExtensions.ComputeBlendsFromOverlaps(track.clips); return newClip; }
// Creates a complete clone of a track and returns it.
// Does not parent, or add the track to the sequence
public static TrackAsset Clone(PlayableAsset parent, TrackAsset trackAsset, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, PlayableAsset assetOwner = null) { if (trackAsset == null) return null;
var timelineAsset = trackAsset.timelineAsset; if (timelineAsset == null) return null;
if (assetOwner == null) assetOwner = parent;
// create a duplicate, then clear the clips and subtracks
var newTrack = Object.Instantiate(trackAsset); newTrack.name = trackAsset.name; newTrack.ClearClipsInternal(); newTrack.parent = parent; newTrack.ClearSubTracksInternal();
if (trackAsset.hasCurves) newTrack.curves = CloneAnimationClip(trackAsset.curves, assetOwner);
var animTrack = trackAsset as AnimationTrack; if (animTrack != null && animTrack.infiniteClip != null) ((AnimationTrack)newTrack).infiniteClip = CloneAnimationClip(animTrack.infiniteClip, assetOwner);
foreach (var clip in trackAsset.clips) { var newClip = DuplicateClip(clip, sourceTable, destTable, assetOwner); newClip.parentTrack = newTrack; }
newTrack.ClearMarkers(); foreach (var e in trackAsset.GetMarkersRaw()) { var newMarker = Object.Instantiate(e); newTrack.AddMarker(newMarker); SaveCloneToAsset(newMarker, assetOwner); if (newMarker is IMarker) { (newMarker as IMarker).Initialize(newTrack); } }
newTrack.SetCollapsed(trackAsset.GetCollapsed());
// calling code is responsible for adding to asset, adding to sequence, and parenting,
// and duplicating subtracks
return newTrack; }
public static IEnumerable<ITimelineItem> DuplicateItemsUsingCurrentEditMode(WindowState state, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, ItemsPerTrack items, TrackAsset targetParent, double candidateTime, string undoOperation) { if (targetParent != null) { var aTrack = targetParent as AnimationTrack; if (aTrack != null) aTrack.ConvertToClipMode();
var duplicatedItems = DuplicateItems(items, targetParent, sourceTable, destTable, undoOperation); FinalizeInsertItemsUsingCurrentEditMode(state, new[] {duplicatedItems}, candidateTime); return duplicatedItems.items; }
return Enumerable.Empty<ITimelineItem>(); }
public static IEnumerable<ITimelineItem> DuplicateItemsUsingCurrentEditMode(WindowState state, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, IEnumerable<ItemsPerTrack> items, double candidateTime, string undoOperation) { var duplicatedItemsGroups = new List<ItemsPerTrack>(); foreach (var i in items) duplicatedItemsGroups.Add(DuplicateItems(i, i.targetTrack, sourceTable, destTable, undoOperation));
FinalizeInsertItemsUsingCurrentEditMode(state, duplicatedItemsGroups, candidateTime); return duplicatedItemsGroups.SelectMany(i => i.items); }
internal static ItemsPerTrack DuplicateItems(ItemsPerTrack items, TrackAsset target, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, string undoOperation) { var duplicatedItems = new List<ITimelineItem>(); var clips = items.clips.ToList(); if (clips.Any()) { TimelineUndo.PushUndo(target, undoOperation); duplicatedItems.AddRange(DuplicateClips(clips, sourceTable, destTable, target).ToItems()); TimelineUndo.PushUndo(target, undoOperation); // second undo causes reference fixups on redo (case 1063868)
}
var markers = items.markers.ToList(); if (markers.Any()) { duplicatedItems.AddRange(MarkerModifier.CloneMarkersToParent(markers, target).ToItems()); }
return new ItemsPerTrack(target, duplicatedItems.ToArray()); }
static void FinalizeInsertItemsUsingCurrentEditMode(WindowState state, IList<ItemsPerTrack> itemsGroups, double candidateTime) { EditMode.FinalizeInsertItemsAtTime(itemsGroups, candidateTime);
SelectionManager.Clear(); foreach (var itemsGroup in itemsGroups) { var track = itemsGroup.targetTrack; var items = itemsGroup.items;
EditModeUtils.SetParentTrack(items, track);
track.SortClips();
TrackExtensions.ComputeBlendsFromOverlaps(track.clips); track.CalculateExtrapolationTimes();
foreach (var item in items) if (item.gui != null) item.gui.Select(); }
var allItems = itemsGroups.SelectMany(x => x.items).ToList(); foreach (var item in allItems) { SelectionManager.Add(item); }
FrameItems(state, allItems); }
internal static TimelineClip Clone(TimelineClip clip, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, PlayableAsset newOwner) { var editorClip = EditorClipFactory.GetEditorClip(clip); // Workaround for Clips not being unity object, assign it to a editor clip wrapper, clone it, and pull the clip back out
var newClip = Object.Instantiate(editorClip).clip;
// perform fix ups for what Instantiate cannot properly detect
SelectionManager.Remove(newClip); newClip.parentTrack = null; newClip.curves = null; // instantiate might copy the reference, we need to clear it
// curves are explicitly owned by the clip
if (clip.curves != null) { newClip.CreateCurves(AnimationTrackRecorder.GetUniqueRecordedClipName(newOwner, clip.curves.name)); EditorUtility.CopySerialized(clip.curves, newClip.curves); TimelineCreateUtilities.SaveAssetIntoObject(newClip.curves, newOwner); }
ScriptableObject playableAsset = newClip.asset as ScriptableObject; if (playableAsset != null && newClip.asset is IPlayableAsset) { var clone = CloneReferencedPlayableAsset(playableAsset, sourceTable, destTable, newOwner); newClip.asset = clone;
// special case to make the name match the recordable clips, but only if they match on the original clip
var originalRecordedAsset = clip.asset as AnimationPlayableAsset; if (clip.recordable && originalRecordedAsset != null && originalRecordedAsset.clip != null) { AnimationPlayableAsset clonedAnimationAsset = clone as AnimationPlayableAsset; if (clonedAnimationAsset != null && clonedAnimationAsset.clip != null) { clonedAnimationAsset.clip = CloneAnimationClip(originalRecordedAsset.clip, newOwner); if (clip.displayName == originalRecordedAsset.clip.name && newClip.recordable) { clonedAnimationAsset.name = clonedAnimationAsset.clip.name; newClip.displayName = clonedAnimationAsset.name; } } } }
return newClip; }
static TimelineClip[] DuplicateClips(IEnumerable<TimelineClip> clips, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, PlayableAsset newOwner) { var newClips = new TimelineClip[clips.Count()];
int i = 0;
foreach (var clip in clips) { var newParent = newOwner == null ? clip.parentTrack : newOwner; var newClip = DuplicateClip(clip, sourceTable, destTable, newParent); newClip.parentTrack = null; newClips[i++] = newClip; }
return newClips; }
static TimelineClip DuplicateClip(TimelineClip clip, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, PlayableAsset newOwner) { var newClip = Clone(clip, sourceTable, destTable, newOwner);
var track = clip.parentTrack; if (track != null) { newClip.parentTrack = track; track.AddClip(newClip); }
var editor = CustomTimelineEditorCache.GetClipEditor(clip); try { editor.OnCreate(newClip, track, clip); } catch (Exception e) { Debug.LogException(e); }
return newClip; }
// Given a track type, return all the playable asset types that should be
// visible to the user via menus
// Given a track type, return all the playable asset types
public static Type GetCustomDrawer(Type trackType) { if (s_SubClassesOfTrackDrawer == null) { s_SubClassesOfTrackDrawer = TypeCache.GetTypesDerivedFrom<TrackDrawer>().ToList(); }
foreach (var drawer in s_SubClassesOfTrackDrawer) { var attr = Attribute.GetCustomAttribute(drawer, typeof(CustomTrackDrawerAttribute), false) as CustomTrackDrawerAttribute; if (attr != null && attr.assetType.IsAssignableFrom(trackType)) return drawer; }
return typeof(TrackDrawer); }
public static bool HaveSameContainerAsset(Object assetA, Object assetB) { if (assetA == null || assetB == null) return false;
if ((assetA.hideFlags & HideFlags.DontSave) != 0 && (assetB.hideFlags & HideFlags.DontSave) != 0) return true;
return AssetDatabase.GetAssetPath(assetA) == AssetDatabase.GetAssetPath(assetB); }
public static void SaveAnimClipIntoObject(AnimationClip clip, Object asset) { if (asset != null) { clip.hideFlags = asset.hideFlags & ~HideFlags.HideInHierarchy; // show animation clips, even if the parent track isn't
if ((clip.hideFlags & HideFlags.DontSave) == 0) { AssetDatabase.AddObjectToAsset(clip, asset); } } }
// Make sure a gameobject has all the required component for the given TrackAsset
public static Component AddRequiredComponent(GameObject go, TrackAsset asset) { if (go == null || asset == null) return null;
var bindings = asset.outputs; if (!bindings.Any()) return null;
var binding = bindings.First(); if (binding.outputTargetType == null || !typeof(Component).IsAssignableFrom(binding.outputTargetType)) return null;
var component = go.GetComponent(binding.outputTargetType); if (component == null) { component = Undo.AddComponent(go, binding.outputTargetType); } return component; }
public static string GetTrackCategoryName(System.Type trackType) { if (trackType == null) return string.Empty;
string s = GetItemCategoryName(trackType); if (!String.IsNullOrEmpty(s)) return s;
if (trackType.Namespace == null || trackType.Namespace.Contains("UnityEngine")) return string.Empty;
return trackType.Namespace + "/"; }
public static string GetItemCategoryName(System.Type itemType) { if (itemType == null) return string.Empty;
var attribute = itemType.GetCustomAttribute(typeof(MenuCategoryAttribute)) as MenuCategoryAttribute; if (attribute != null) { var s = attribute.category; if (!s.EndsWith("/")) s += "/"; return s; }
return string.Empty; }
public static string GetTrackMenuName(System.Type trackType) { return ObjectNames.NicifyVariableName(trackType.Name); }
// retrieve the duration of a single loop, taking into account speed
public static double GetLoopDuration(TimelineClip clip) { double length = clip.clipAssetDuration; if (double.IsNegativeInfinity(length) || double.IsNaN(length)) return TimelineClip.kMinDuration;
if (length == double.MaxValue || double.IsInfinity(length)) { return double.MaxValue; }
return Math.Max(TimelineClip.kMinDuration, length / clip.timeScale); }
public static double GetClipAssetEndTime(TimelineClip clip) { var d = GetLoopDuration(clip); if (d < double.MaxValue) d = clip.FromLocalTimeUnbound(d);
return d; }
// Checks if the underlying asset duration is usable. This means the clip
// can loop or hold
public static bool HasUsableAssetDuration(TimelineClip clip) { double length = clip.clipAssetDuration; return (length < TimelineClip.kMaxTimeValue) && !double.IsInfinity(length) && !double.IsNaN(length); }
// Retrieves the starting point of each loop of a clip, relative to the start of the clip
// Note that if clip-in is bigger than the loopDuration, negative times will be added
public static double[] GetLoopTimes(TimelineClip clip) { if (!HasUsableAssetDuration(clip)) return new[] {-clip.clipIn / clip.timeScale};
var times = new List<double>(); double loopDuration = GetLoopDuration(clip);
if (loopDuration <= TimeUtility.kTimeEpsilon) return new double[] {};
double start = -clip.clipIn / clip.timeScale; double end = start + loopDuration;
times.Add(start); while (end < clip.duration - WindowState.kTimeEpsilon) { times.Add(end); end += loopDuration; }
return times.ToArray(); }
public static double GetCandidateTime(WindowState state, Vector2? mousePosition, params TrackAsset[] trackAssets) { // Right-Click
if (mousePosition != null) return state.GetSnappedTimeAtMousePosition(mousePosition.Value);
// Playhead
if (state != null && state.editSequence.director != null) return state.SnapToFrameIfRequired(state.editSequence.time);
// Specific tracks end
if (trackAssets != null && trackAssets.Any()) { var items = trackAssets.SelectMany(t => t.GetItems()).ToList(); return items.Any() ? items.Max(i => i.end) : 0; }
// Timeline tracks end
if (state != null && state.editSequence.asset != null) return state.editSequence.asset.flattenedTracks.Any() ? state.editSequence.asset.flattenedTracks.Max(t => t.end) : 0;
return 0.0; }
public static TimelineClip CreateClipOnTrack(Object asset, TrackAsset parentTrack, WindowState state) { return CreateClipOnTrack(asset, parentTrack, GetCandidateTime(state, null, parentTrack), state); }
public static TimelineClip CreateClipOnTrack(Object asset, TrackAsset parentTrack, double candidateTime) { WindowState state = null; if (TimelineWindow.instance != null) state = TimelineWindow.instance.state;
return CreateClipOnTrack(asset, parentTrack, candidateTime, state); }
public static TimelineClip CreateClipOnTrack(Type playableAssetType, TrackAsset parentTrack, WindowState state) { return CreateClipOnTrack(playableAssetType, null, parentTrack, GetCandidateTime(state, null, parentTrack), state); }
public static TimelineClip CreateClipOnTrack(Type playableAssetType, TrackAsset parentTrack, double candidateTime) { return CreateClipOnTrack(playableAssetType, null, parentTrack, candidateTime); }
public static TimelineClip CreateClipOnTrack(Object asset, TrackAsset parentTrack, double candidateTime, WindowState state) { if (parentTrack == null) return null;
// pick the first clip type available, unless there is one that matches the asset
var clipType = TypeUtility.GetPlayableAssetsHandledByTrack(parentTrack.GetType()).FirstOrDefault(); if (asset != null) clipType = TypeUtility.GetAssetTypesForObject(parentTrack.GetType(), asset).FirstOrDefault();
if (clipType == null) return null;
return CreateClipOnTrack(clipType, asset, parentTrack, candidateTime, state); }
public static TimelineClip CreateClipOnTrack(Type playableAssetType, Object assignableObject, TrackAsset parentTrack, double candidateTime) { WindowState state = null; if (TimelineWindow.instance != null) state = TimelineWindow.instance.state;
return CreateClipOnTrack(playableAssetType, assignableObject, parentTrack, candidateTime, state); }
public static TimelineClip CreateClipOnTrack(Type playableAssetType, Object assignableObject, TrackAsset parentTrack, double candidateTime, WindowState state) { if (parentTrack == null) return null;
bool revertClipMode = false;
// Ideally this is done automatically by the animation track,
// but it's editor only because it does animation clip manipulation
var animTrack = parentTrack as AnimationTrack; if (animTrack != null && animTrack.CanConvertToClipMode()) { animTrack.ConvertToClipMode(); revertClipMode = true; }
TimelineClip newClip = null; if (TypeUtility.IsConcretePlayableAsset(playableAssetType)) { try { newClip = parentTrack.CreateClipOfType(playableAssetType); } catch (InvalidOperationException) {} // expected on a mismatch
}
if (newClip == null) { if (revertClipMode) animTrack.ConvertFromClipMode(animTrack.timelineAsset);
Debug.LogWarningFormat("Cannot create a clip of type {0} on a track of type {1}", playableAssetType.Name, parentTrack.GetType().Name); return null; }
AddClipOnTrack(newClip, parentTrack, candidateTime, assignableObject, state);
return newClip; }
/// <summary>
/// Create a clip on track from an existing PlayableAsset
/// </summary>
public static TimelineClip CreateClipOnTrackFromPlayableAsset(IPlayableAsset asset, TrackAsset parentTrack, double candidateTime) { if (parentTrack == null || asset == null || !TypeUtility.IsConcretePlayableAsset(asset.GetType())) return null;
TimelineClip newClip = null; try { newClip = parentTrack.CreateClipFromPlayableAsset(asset); } catch { return null; }
WindowState state = null; if (TimelineWindow.instance != null) state = TimelineWindow.instance.state;
AddClipOnTrack(newClip, parentTrack, candidateTime, null, state);
return newClip; }
public static void CreateClipsFromObjects(Type assetType, TrackAsset targetTrack, double candidateTime, IEnumerable<Object> objects) { foreach (var obj in objects) { if (ObjectReferenceField.FindObjectReferences(assetType).Any(f => f.IsAssignable(obj))) { var clip = CreateClipOnTrack(assetType, obj, targetTrack, candidateTime); candidateTime += clip.duration; } } }
public static void CreateMarkersFromObjects(Type assetType, TrackAsset targetTrack, double candidateTime, IEnumerable<Object> objects) { var mList = new List<ITimelineItem>(); foreach (var obj in objects) { if (ObjectReferenceField.FindObjectReferences(assetType).Any(f => f.IsAssignable(obj))) { var marker = CreateMarkerOnTrack(assetType, obj, targetTrack, candidateTime); mList.Add(marker.ToItem()); } }
var state = TimelineWindow.instance.state; for (var i = 1; i < mList.Count; ++i) { var delta = ItemsUtils.TimeGapBetweenItems(mList[i - 1], mList[i], state); mList[i].start += delta; }
FinalizeInsertItemsUsingCurrentEditMode(state, new[] {new ItemsPerTrack(targetTrack, mList)}, candidateTime); state.Refresh(); }
public static IMarker CreateMarkerOnTrack(Type markerType, Object assignableObject, TrackAsset parentTrack, double candidateTime) { WindowState state = null; if (TimelineWindow.instance != null) state = TimelineWindow.instance.state;
var newMarker = parentTrack.CreateMarker(markerType, candidateTime); //Throws if marker is not an object
var obj = newMarker as ScriptableObject; if (obj != null) obj.name = TypeUtility.GetDisplayName(markerType);
if (assignableObject != null) { var director = state != null ? state.editSequence.director : null; foreach (var field in ObjectReferenceField.FindObjectReferences(markerType)) { if (field.IsAssignable(assignableObject)) { field.Assign(newMarker as ScriptableObject, assignableObject, director); break; } } }
try { CustomTimelineEditorCache.GetMarkerEditor(newMarker).OnCreate(newMarker, null); } catch (Exception e) { Debug.LogException(e); }
return newMarker; }
public static void CreateClipsFromTypes(IEnumerable<Type> assetTypes, TrackAsset targetTrack, double candidateTime) { foreach (var assetType in assetTypes) { var clip = CreateClipOnTrack(assetType, targetTrack, candidateTime); candidateTime += clip.duration; } }
public static void FrameItems(WindowState state, IEnumerable<ITimelineItem> items) { if (items == null || !items.Any() || state == null) return;
// if this is called before a repaint, the timeArea can be null
var window = state.editorWindow as TimelineWindow; if (window == null || window.timeArea == null) return;
var start = (float)items.Min(x => x.start); var end = (float)items.Max(x => x.end); var timeRange = state.timeAreaShownRange;
// nothing to do
if (timeRange.x <= start && timeRange.y >= end) return;
var ds = start - timeRange.x; var de = end - timeRange.y;
var padding = state.PixelDeltaToDeltaTime(15); var d = Math.Abs(ds) < Math.Abs(de) ? ds - padding : de + padding;
state.SetTimeAreaShownRange(timeRange.x + d, timeRange.y + d); }
public static void Frame(WindowState state, double start, double end) { var timeRange = state.timeAreaShownRange;
// nothing to do
if (timeRange.x <= start && timeRange.y >= end) return;
var ds = (float)start - timeRange.x; var de = (float)end - timeRange.y;
var padding = state.PixelDeltaToDeltaTime(15); var d = Math.Abs(ds) < Math.Abs(de) ? ds - padding : de + padding;
state.SetTimeAreaShownRange(timeRange.x + d, timeRange.y + d); }
public static void RangeSelect<T>(IList<T> totalCollection, IList<T> currentSelection, T clickedItem, Action<T> selector, Action<T> remover) where T : class { var firstSelect = currentSelection.FirstOrDefault(); if (firstSelect == null) { selector(clickedItem); return; }
var idxFirstSelect = totalCollection.IndexOf(firstSelect); var idxLastSelect = totalCollection.IndexOf(currentSelection.Last()); var idxClicked = totalCollection.IndexOf(clickedItem);
//case 927807: selection is invalid
if (idxFirstSelect < 0) { SelectionManager.Clear(); selector(clickedItem); return; }
// Expand the selection between the first selected clip and clicked clip (insertion order is important)
if (idxFirstSelect < idxClicked) for (var i = idxFirstSelect; i <= idxClicked; ++i) selector(totalCollection[i]); else for (var i = idxFirstSelect; i >= idxClicked; --i) selector(totalCollection[i]);
// If clicked inside the selected range, shrink the selection between the the click and last selected clip
if (Math.Min(idxFirstSelect, idxLastSelect) < idxClicked && idxClicked < Math.Max(idxFirstSelect, idxLastSelect)) for (var i = Math.Min(idxLastSelect, idxClicked); i <= Math.Max(idxLastSelect, idxClicked); ++i) remover(totalCollection[i]);
// Ensure clicked clip is selected
selector(clickedItem); }
public static void Bind(TrackAsset track, Object obj, PlayableDirector director) { if (director != null && track != null) { var bindType = TypeUtility.GetTrackBindingAttribute(track.GetType()); if (bindType == null || bindType.type == null) return;
if (obj == null || bindType.type.IsInstanceOfType(obj)) { TimelineUndo.PushUndo(director, "Bind Track"); director.SetGenericBinding(track, obj); } else if (obj is GameObject && typeof(Component).IsAssignableFrom(bindType.type)) { var component = (obj as GameObject).GetComponent(bindType.type); if (component == null) component = Undo.AddComponent(obj as GameObject, bindType.type);
TimelineUndo.PushUndo(director, "Bind Track"); director.SetGenericBinding(track, component); } } }
/// <summary>
/// Shared code for adding a clip to a track
/// </summary>
static void AddClipOnTrack(TimelineClip newClip, TrackAsset parentTrack, double candidateTime, Object assignableObject, WindowState state) { var playableAsset = newClip.asset as IPlayableAsset;
newClip.parentTrack = null; newClip.timeScale = 1.0; newClip.mixInCurve = AnimationCurve.EaseInOut(0, 0, 1, 1); newClip.mixOutCurve = AnimationCurve.EaseInOut(0, 1, 1, 0);
var playableDirector = state != null ? state.editSequence.director : null;
if (assignableObject != null) { foreach (var field in ObjectReferenceField.FindObjectReferences(playableAsset.GetType())) { if (field.IsAssignable(assignableObject)) { newClip.displayName = assignableObject.name; field.Assign(newClip.asset as PlayableAsset, assignableObject, playableDirector); break; } } }
// get the clip editor
try { CustomTimelineEditorCache.GetClipEditor(newClip).OnCreate(newClip, parentTrack, null); } catch (Exception e) { Debug.LogException(e); }
// reset the duration as the newly assigned values may have changed the default
if (playableAsset != null) { var candidateDuration = playableAsset.duration;
if (!double.IsInfinity(candidateDuration) && candidateDuration > 0) newClip.duration = Math.Min(Math.Max(candidateDuration, TimelineClip.kMinDuration), TimelineClip.kMaxTimeValue); }
var newClipsByTracks = new[] { new ItemsPerTrack(parentTrack, new[] {newClip.ToItem()}) };
FinalizeInsertItemsUsingCurrentEditMode(state, newClipsByTracks, candidateTime);
if (state != null) state.Refresh(); }
public static TrackAsset CreateTrack(TimelineAsset asset, Type type, TrackAsset parent = null, string name = null) { if (asset == null) return null;
var track = asset.CreateTrack(type, parent, name); if (track != null) { if (parent != null) parent.SetCollapsed(false);
var editor = CustomTimelineEditorCache.GetTrackEditor(track); try { editor.OnCreate(track, null); } catch (Exception e) { Debug.LogException(e); } TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved); }
return track; }
public static TrackAsset CreateTrack(Type type, TrackAsset parent = null, string name = null) { return CreateTrack(TimelineEditor.inspectedAsset, type, parent, name); }
public static T CreateTrack<T>(TimelineAsset asset, TrackAsset parent = null, string name = null) where T : TrackAsset { return (T)CreateTrack(asset, typeof(T), parent, name); }
public static T CreateTrack<T>(TrackAsset parent = null, string name = null) where T : TrackAsset { return (T)CreateTrack(TimelineEditor.inspectedAsset, typeof(T), parent, name); } }}
|