using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Timeline; using UnityEngine.Playables; using UnityEngineInternal; // for metro type extensions namespace UnityEngine.Timeline { public partial class TimelineAsset { /// /// Allows you to create a track and add it to the Timeline. /// /// The type of track to create. Must derive from TrackAsset. /// Track to parent to. This can be null. /// Name to give the track. /// The created track. /// /// This method will throw an InvalidOperationException if the parent is not valid. The parent can be any GroupTrack, or a supported parent type of track. For example, this can be used to create override tracks in AnimationTracks. /// public TrackAsset CreateTrack(Type type, TrackAsset parent, string name) { if (parent != null && parent.timelineAsset != this) throw new InvalidOperationException("Addtrack cannot parent to a track not in the Timeline"); if (!typeof(TrackAsset).IsAssignableFrom(type)) throw new InvalidOperationException("Supplied type must be a track asset"); if (parent != null) { if (!TimelineCreateUtilities.ValidateParentTrack(parent, type)) throw new InvalidOperationException("Cannot assign a child of type " + type.Name + " to a parent of type " + parent.GetType().Name); } var baseName = name; if (string.IsNullOrEmpty(baseName)) { baseName = type.Name; #if UNITY_EDITOR baseName = UnityEditor.ObjectNames.NicifyVariableName(baseName); #endif } var trackName = baseName; if (parent != null) trackName = TimelineCreateUtilities.GenerateUniqueActorName(parent.subTracksObjects, baseName); else trackName = TimelineCreateUtilities.GenerateUniqueActorName(trackObjects, baseName); TrackAsset newTrack = AllocateTrack(parent, trackName, type); return newTrack; } /// /// Creates a track and adds it to the Timeline Asset. /// /// Track to parent to. This can be null. /// The name of the track being created. /// The type of track being created. The track type must be derived from TrackAsset. /// Returns the created track. /// /// This method will throw an InvalidOperationException if the parent is not valid. The parent can be any GroupTrack, or a supported parent type of track. For example, this can be used to create override tracks in AnimationTracks. /// public T CreateTrack(TrackAsset parent, string trackName) where T : TrackAsset, new() { return (T)CreateTrack(typeof(T), parent, trackName); } /// /// Creates a track and adds it to the Timeline Asset. /// /// The name of the track being created. /// The type of track being created. The track type must be derived from TrackAsset. /// Returns the created track. public T CreateTrack(string trackName) where T : TrackAsset, new() { return (T)CreateTrack(typeof(T), null, trackName); } /// /// Creates a track and adds it to the Timeline Asset. /// /// The type of track being created. The track type must be derived from TrackAsset. /// Returns the created track. public T CreateTrack() where T : TrackAsset, new() { return (T)CreateTrack(typeof(T), null, null); } /// /// Delete a clip from this timeline. /// /// The clip to delete. /// Returns true if the removal was successful /// /// This method will delete a clip and any assets owned by the clip. /// public bool DeleteClip(TimelineClip clip) { if (clip == null || clip.GetParentTrack() == null) { return false; } if (this != clip.GetParentTrack().timelineAsset) { Debug.LogError("Cannot delete a clip from this timeline"); return false; } TimelineUndo.PushUndo(clip.GetParentTrack(), "Delete Clip"); if (clip.curves != null) { TimelineUndo.PushDestroyUndo(this, clip.GetParentTrack(), clip.curves); } // handle wrapped assets if (clip.asset != null) { DeleteRecordedAnimation(clip); // TODO -- we should flag assets and owned, instead of this check... #if UNITY_EDITOR string path = UnityEditor.AssetDatabase.GetAssetPath(clip.asset); if (path == UnityEditor.AssetDatabase.GetAssetPath(this)) #endif { TimelineUndo.PushDestroyUndo(this, clip.GetParentTrack(), clip.asset); } } var clipParentTrack = clip.GetParentTrack(); clipParentTrack.RemoveClip(clip); clipParentTrack.CalculateExtrapolationTimes(); return true; } /// /// Deletes a track from a timeline, including all clips and subtracks. /// /// The track to delete. It must be owned by this Timeline. /// True if the track was deleted successfully. public bool DeleteTrack(TrackAsset track) { if (track.timelineAsset != this) return false; // push before we modify properties TimelineUndo.PushUndo(track, "Delete Track"); TimelineUndo.PushUndo(this, "Delete Track"); TrackAsset parent = track.parent as TrackAsset; if (parent != null) TimelineUndo.PushUndo(parent, "Delete Track"); var children = track.GetChildTracks(); foreach (var child in children) { DeleteTrack(child); } DeleteRecordedAnimation(track); var clipsToDelete = new List(track.clips); foreach (var clip in clipsToDelete) { DeleteClip(clip); } RemoveTrack(track); TimelineUndo.PushDestroyUndo(this, this, track); return true; } internal void MoveLastTrackBefore(TrackAsset asset) { if (m_Tracks == null || m_Tracks.Count < 2 || asset == null) return; var lastTrack = m_Tracks[m_Tracks.Count - 1]; if (lastTrack == asset) return; for (int i = 0; i < m_Tracks.Count - 1; i++) { if (m_Tracks[i] == asset) { for (int j = m_Tracks.Count - 1; j > i; j--) m_Tracks[j] = m_Tracks[j - 1]; m_Tracks[i] = lastTrack; Invalidate(); break; } } } TrackAsset AllocateTrack(TrackAsset trackAssetParent, string trackName, Type trackType) { if (trackAssetParent != null && trackAssetParent.timelineAsset != this) throw new InvalidOperationException("Addtrack cannot parent to a track not in the Timeline"); if (!typeof(TrackAsset).IsAssignableFrom(trackType)) throw new InvalidOperationException("Supplied type must be a track asset"); var asset = (TrackAsset)CreateInstance(trackType); asset.name = trackName; const string createTrackUndoName = "Create Track"; PlayableAsset parent = trackAssetParent != null ? trackAssetParent as PlayableAsset : this; TimelineCreateUtilities.SaveAssetIntoObject(asset, parent); TimelineUndo.RegisterCreatedObjectUndo(asset, createTrackUndoName); TimelineUndo.PushUndo(parent, createTrackUndoName); if (trackAssetParent != null) trackAssetParent.AddChild(asset); else //TimelineAsset is the parent AddTrackInternal(asset); return asset; } void DeleteRecordedAnimation(TrackAsset track) { var animTrack = track as AnimationTrack; if (animTrack != null && animTrack.infiniteClip != null) TimelineUndo.PushDestroyUndo(this, track, animTrack.infiniteClip); if (track.curves != null) TimelineUndo.PushDestroyUndo(this, track, track.curves); } void DeleteRecordedAnimation(TimelineClip clip) { if (clip == null) return; if (clip.curves != null) TimelineUndo.PushDestroyUndo(this, clip.GetParentTrack(), clip.curves); if (!clip.recordable) return; AnimationPlayableAsset asset = clip.asset as AnimationPlayableAsset; if (asset == null || asset.clip == null) return; TimelineUndo.PushDestroyUndo(this, asset, asset.clip); } } }