// Special method to remove a track that is in a broken state. i.e. the script won't load internal static bool RemoveBrokenTrack(PlayableAsset parent, ScriptableObject track) { var parentTrack = parent as TrackAsset; var parentTimeline = parent as TimelineAsset; if (parentTrack == null && parentTimeline == null) { throw new ArgumentException("parent is not a valid parent type", "parent"); } // this object must be a Unity null, but not actually null; object trackAsObject = track; if (trackAsObject == null || track as TrackAsset != null) // yes, this is correct { throw new ArgumentException("track is not in a broken state"); } // this belongs to a parent track if (parentTrack != null) { int index = parentTrack.subTracksObjects.FindIndex(t => t.GetInstanceID() == track.GetInstanceID()); if (index >= 0) { UndoExtensions.RegisterTrack(parentTrack, L10n.Tr("Remove Track")); parentTrack.subTracksObjects.RemoveAt(index); parentTrack.Invalidate(); Undo.DestroyObjectImmediate(track); return(true); } } else if (parentTimeline != null) { int index = parentTimeline.trackObjects.FindIndex(t => t.GetInstanceID() == track.GetInstanceID()); if (index >= 0) { UndoExtensions.RegisterPlayableAsset(parentTimeline, L10n.Tr("Remove Track")); parentTimeline.trackObjects.RemoveAt(index); parentTimeline.Invalidate(); Undo.DestroyObjectImmediate(track); return(true); } } return(false); }
void DoManipulators() { if (m_EditorClip == null || m_EditorClip.clip == null) { return; } AnimationPlayableAsset animationPlayable = m_EditorClip.clip.asset as AnimationPlayableAsset; AnimationTrack track = m_EditorClip.clip.parentTrack as AnimationTrack; Transform transform = GetTransform(); if (transform != null && animationPlayable != null && m_OffsetEditMode != TimelineAnimationUtilities.OffsetEditMode.None && track != null) { UndoExtensions.RegisterPlayableAsset(animationPlayable, "Inspector"); Vector3 position = transform.position; Quaternion rotation = transform.rotation; EditorGUI.BeginChangeCheck(); if (m_OffsetEditMode == TimelineAnimationUtilities.OffsetEditMode.Translation) { position = Handles.PositionHandle(position, Tools.pivotRotation == PivotRotation.Global ? Quaternion.identity : rotation); } else if (m_OffsetEditMode == TimelineAnimationUtilities.OffsetEditMode.Rotation) { rotation = Handles.RotationHandle(rotation, position); } if (EditorGUI.EndChangeCheck()) { var res = TimelineAnimationUtilities.UpdateClipOffsets(animationPlayable, track, transform, position, rotation); animationPlayable.position = res.position; animationPlayable.eulerAngles = AnimationUtility.GetClosestEuler(res.rotation, animationPlayable.eulerAngles, RotationOrder.OrderZXY); Reevaluate(); Repaint(); } } }
// Reparents a list of tracks to a new parent // the new parent cannot be null (has to be track asset or sequence) // the insertAfter can be null (will not reorder) internal static bool ReparentTracks(List <TrackAsset> tracksToMove, PlayableAsset targetParent, TrackAsset insertMarker = null, bool insertBefore = false) { var targetParentTrack = targetParent as TrackAsset; var targetSequenceTrack = targetParent as TimelineAsset; if (tracksToMove == null || tracksToMove.Count == 0 || (targetParentTrack == null && targetSequenceTrack == null)) { return(false); } // invalid parent type on a track if (targetParentTrack != null && tracksToMove.Any(x => !TimelineCreateUtilities.ValidateParentTrack(targetParentTrack, x.GetType()))) { return(false); } // no valid tracks means this is simply a rearrangement var validTracks = tracksToMove.Where(x => x.parent != targetParent).ToList(); if (insertMarker == null && !validTracks.Any()) { return(false); } var parents = validTracks.Select(x => x.parent).Where(x => x != null).Distinct().ToList(); // push the current state of the tracks that will change foreach (var p in parents) { UndoExtensions.RegisterPlayableAsset(p, "Reparent"); } UndoExtensions.RegisterTracks(validTracks, "Reparent"); UndoExtensions.RegisterPlayableAsset(targetParent, "Reparent"); // need to reparent tracks first, before moving them. foreach (var t in validTracks) { if (t.parent != targetParent) { TrackAsset toMoveParent = t.parent as TrackAsset; TimelineAsset toMoveTimeline = t.parent as TimelineAsset; if (toMoveTimeline != null) { toMoveTimeline.RemoveTrack(t); } else if (toMoveParent != null) { toMoveParent.RemoveSubTrack(t); } if (targetParentTrack != null) { targetParentTrack.AddChild(t); targetParentTrack.SetCollapsed(false); } else { targetSequenceTrack.AddTrackInternal(t); } } } if (insertMarker != null) { // re-ordering track. This is using internal APIs, so invalidation of the tracks must be done manually to avoid // cache mismatches var children = targetParentTrack != null ? targetParentTrack.subTracksObjects : targetSequenceTrack.trackObjects; TimelineUtility.ReorderTracks(children, tracksToMove, insertMarker, insertBefore); if (targetParentTrack != null) { targetParentTrack.Invalidate(); } if (insertMarker.timelineAsset != null) { insertMarker.timelineAsset.Invalidate(); } } return(true); }
internal static TrackAsset Duplicate(this TrackAsset track, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, TimelineAsset destinationTimeline = null) { if (track == null) { return(null); } // if the destination is us, clear to avoid bad parenting (case 919421) if (destinationTimeline == track.timelineAsset) { destinationTimeline = null; } var timelineParent = track.parent as TimelineAsset; var trackParent = track.parent as TrackAsset; if (timelineParent == null && trackParent == null) { Debug.LogWarning("Cannot duplicate track because it is not parented to known type"); return(null); } // Determine who the final parent is. If we are pasting into another track, it's always the timeline. // Otherwise it's the original parent PlayableAsset finalParent = destinationTimeline != null ? destinationTimeline : track.parent; // grab the list of tracks to generate a name from (923360) to get the list of names // no need to do this part recursively var finalTrackParent = finalParent as TrackAsset; var finalTimelineAsset = finalParent as TimelineAsset; var otherTracks = (finalTimelineAsset != null) ? finalTimelineAsset.trackObjects : finalTrackParent.subTracksObjects; // Important to create the new objects before pushing the original undo, or redo breaks the // sequence var newTrack = TimelineHelpers.Clone(finalParent, track, sourceTable, destTable, finalParent); newTrack.name = TimelineCreateUtilities.GenerateUniqueActorName(otherTracks, newTrack.name); RecursiveSubtrackClone(track, newTrack, sourceTable, destTable, finalParent); TimelineCreateUtilities.SaveAssetIntoObject(newTrack, finalParent); TimelineUndo.RegisterCreatedObjectUndo(newTrack, L10n.Tr("Duplicate")); UndoExtensions.RegisterPlayableAsset(finalParent, L10n.Tr("Duplicate")); if (destinationTimeline != null) // other timeline { destinationTimeline.AddTrackInternal(newTrack); } else if (timelineParent != null) // this timeline, no parent { ReparentTracks(new List <TrackAsset> { newTrack }, timelineParent, timelineParent.GetRootTracks().Last(), false); } else // this timeline, with parent { trackParent.AddChild(newTrack); } // Call the custom editor. this check prevents the call when copying to the clipboard if (destinationTimeline == null || destinationTimeline == TimelineEditor.inspectedAsset) { var customEditor = CustomTimelineEditorCache.GetTrackEditor(newTrack); customEditor.OnCreate_Safe(newTrack, track); } return(newTrack); }