123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- using System;
- using System.Collections.Generic;
- using UnityEditor.Callbacks;
- using UnityEngine;
- using UnityEngine.Events;
- using UnityEngine.Playables;
- using UnityEngine.SceneManagement;
- using UnityEngine.Timeline;
- namespace UnityEditor.Timeline
- {
- [EditorWindowTitle(title = "Timeline", useTypeNameAsIconName = true)]
- partial class TimelineWindow : EditorWindow, IHasCustomMenu
- {
- [Serializable]
- public class TimelineWindowPreferences
- {
- public bool playRangeLoopMode = true;
- public EditMode.EditType editType = EditMode.EditType.Mix;
- public TimeReferenceMode timeReferenceMode = TimeReferenceMode.Local;
- }
- [SerializeField] TimelineWindowPreferences m_Preferences = new TimelineWindowPreferences();
- public TimelineWindowPreferences preferences { get { return m_Preferences; } }
- [SerializeField]
- EditorGUIUtility.EditorLockTracker m_LockTracker = new EditorGUIUtility.EditorLockTracker();
- readonly PreviewResizer m_PreviewResizer = new PreviewResizer();
- bool m_LastFrameHadSequence;
- bool m_ForceRefreshLastSelection;
- int m_CurrentSceneHashCode = -1;
- [NonSerialized]
- bool m_HasBeenInitialized;
- [SerializeField]
- SequenceHierarchy m_SequenceHierarchy;
- static SequenceHierarchy s_LastHierarchy;
- public static TimelineWindow instance { get; private set; }
- public Rect clientArea { get; set; }
- public bool isDragging { get; set; }
- public static DirectorStyles styles { get { return DirectorStyles.Instance; } }
- public List<TimelineTrackBaseGUI> allTracks
- {
- get
- {
- return treeView != null ? treeView.allTrackGuis : new List<TimelineTrackBaseGUI>();
- }
- }
- public WindowState state { get; private set; }
- public bool locked
- {
- get
- {
- // we can never be in a locked state if there is no timeline asset
- if (state.editSequence.asset == null)
- return false;
- return m_LockTracker.isLocked;
- }
- set { m_LockTracker.isLocked = value; }
- }
- public bool hierarchyChangedThisFrame { get; private set; }
- public TimelineWindow()
- {
- InitializeManipulators();
- m_LockTracker.lockStateChanged.AddPersistentListener(OnLockStateChanged, UnityEventCallState.EditorAndRuntime);
- }
- void OnLockStateChanged(bool locked)
- {
- // Make sure that upon unlocking, any selection change is updated
- // Case 1123119 -- only force rebuild if not recording
- if (!locked)
- RefreshSelection(state != null && !state.recording);
- }
- void OnEnable()
- {
- if (m_SequencePath == null)
- m_SequencePath = new SequencePath();
- if (m_SequenceHierarchy == null)
- {
- // The sequence hierarchy will become null if maximize on play is used for in/out of playmode
- // a static var will hang on to the reference
- if (s_LastHierarchy != null)
- m_SequenceHierarchy = s_LastHierarchy;
- else
- m_SequenceHierarchy = SequenceHierarchy.CreateInstance();
- state = null;
- }
- s_LastHierarchy = m_SequenceHierarchy;
- titleContent = GetLocalizedTitleContent();
- m_PreviewResizer.Init("TimelineWindow");
- // Unmaximize fix : when unmaximizing, a new window is enabled and disabled. Prevent it from overriding the instance pointer.
- if (instance == null)
- instance = this;
- AnimationClipCurveCache.Instance.OnEnable();
- TrackAsset.OnClipPlayableCreate += m_PlayableLookup.UpdatePlayableLookup;
- TrackAsset.OnTrackAnimationPlayableCreate += m_PlayableLookup.UpdatePlayableLookup;
- if (state == null)
- {
- state = new WindowState(this, s_LastHierarchy);
- Initialize();
- RefreshSelection(true);
- m_ForceRefreshLastSelection = true;
- }
- }
- void OnDisable()
- {
- if (instance == this)
- instance = null;
- if (state != null)
- state.Reset();
- if (instance == null)
- SelectionManager.RemoveTimelineSelection();
- AnimationClipCurveCache.Instance.OnDisable();
- TrackAsset.OnClipPlayableCreate -= m_PlayableLookup.UpdatePlayableLookup;
- TrackAsset.OnTrackAnimationPlayableCreate -= m_PlayableLookup.UpdatePlayableLookup;
- TimelineWindowViewPrefs.SaveAll();
- TimelineWindowViewPrefs.UnloadAllViewModels();
- }
- void OnDestroy()
- {
- if (state != null)
- {
- state.OnDestroy();
- }
- m_HasBeenInitialized = false;
- RemoveEditorCallbacks();
- AnimationClipCurveCache.Instance.Clear();
- TimelineAnimationUtilities.UnlinkAnimationWindow();
- }
- void OnLostFocus()
- {
- isDragging = false;
- if (state != null)
- state.captured.Clear();
- Repaint();
- }
- void OnFocus()
- {
- if (state == null) return;
- if (lastSelectedGO != Selection.activeObject)
- {
- // selection may have changed while Timeline Editor was looking away
- RefreshSelection(false);
- }
- }
- void OnHierarchyChange()
- {
- hierarchyChangedThisFrame = true;
- Repaint();
- }
- void OnStateChange()
- {
- state.UpdateRecordingState();
- if (treeView != null && state.editSequence.asset != null)
- treeView.Reload();
- if (m_MarkerHeaderGUI != null)
- m_MarkerHeaderGUI.Rebuild();
- }
- void OnGUI()
- {
- InitializeGUIIfRequired();
- UpdateGUIConstants();
- UpdateViewStateHash();
- EditMode.HandleModeClutch(); // TODO We Want that here?
- DetectStylesChange();
- DetectActiveSceneChanges();
- DetectStateChanges();
- state.ProcessStartFramePendingUpdates();
- var clipRect = new Rect(0.0f, 0.0f, position.width, position.height);
- using (new GUIViewportScope(clipRect))
- state.InvokeWindowOnGuiStarted(Event.current);
- if (Event.current.type == EventType.MouseDrag && state != null && state.mouseDragLag > 0.0f)
- {
- state.mouseDragLag -= Time.deltaTime;
- return;
- }
- if (PerformUndo())
- return;
- if (state != null && state.ignorePreview && state.playing)
- {
- if (state.recording)
- state.recording = false;
- Repaint();
- }
- clientArea = position;
- PlaybackScroller.AutoScroll(state);
- DoLayout();
- // overlays
- if (state.captured.Count > 0)
- {
- using (new GUIViewportScope(clipRect))
- {
- foreach (var o in state.captured)
- {
- o.Overlay(Event.current, state);
- }
- Repaint();
- }
- }
- if (state.showQuadTree)
- {
- var fillColor = new Color(1.0f, 1.0f, 1.0f, 0.1f);
- state.spacePartitioner.DebugDraw(fillColor, Color.yellow);
- state.headerSpacePartitioner.DebugDraw(fillColor, Color.green);
- }
- // attempt another rebuild -- this will avoid 1 frame flashes
- if (Event.current.type == EventType.Repaint)
- {
- RebuildGraphIfNecessary();
- state.ProcessEndFramePendingUpdates();
- }
- using (new GUIViewportScope(clipRect))
- {
- if (Event.current.type == EventType.Repaint)
- EditMode.inputHandler.OnGUI(state, Event.current);
- }
- if (Event.current.type == EventType.Repaint)
- hierarchyChangedThisFrame = false;
- }
- static void DetectStylesChange()
- {
- DirectorStyles.ReloadStylesIfNeeded();
- }
- void DetectActiveSceneChanges()
- {
- if (m_CurrentSceneHashCode == -1)
- {
- m_CurrentSceneHashCode = SceneManager.GetActiveScene().GetHashCode();
- }
- if (m_CurrentSceneHashCode != SceneManager.GetActiveScene().GetHashCode())
- {
- bool isSceneStillLoaded = false;
- for (int a = 0; a < SceneManager.sceneCount; a++)
- {
- var scene = SceneManager.GetSceneAt(a);
- if (scene.GetHashCode() == m_CurrentSceneHashCode && scene.isLoaded)
- {
- isSceneStillLoaded = true;
- break;
- }
- }
- if (!isSceneStillLoaded)
- {
- if (!locked)
- ClearCurrentTimeline();
- m_CurrentSceneHashCode = SceneManager.GetActiveScene().GetHashCode();
- }
- }
- }
- void DetectStateChanges()
- {
- if (state != null)
- {
- state.editSequence.ResetIsReadOnly(); //Force reset readonly for asset flag for each frame.
- // detect if the sequence was removed under our feet
- if (m_LastFrameHadSequence && state.editSequence.asset == null)
- {
- ClearCurrentTimeline();
- }
- m_LastFrameHadSequence = state.editSequence.asset != null;
- // the currentDirector can get set to null by a deletion or scene unloading so polling is required
- if (state.editSequence.director == null)
- {
- state.recording = false;
- state.previewMode = false;
- if (!locked && m_LastFrameHadSequence)
- {
- // the user may be adding a new PlayableDirector to a selected GameObject, make sure the timeline editor is shows the proper director if none is already showing
- var selectedGameObject = Selection.activeObject != null ? Selection.activeObject as GameObject : null;
- var selectedDirector = selectedGameObject != null ? selectedGameObject.GetComponent<PlayableDirector>() : null;
- if (selectedDirector != null)
- {
- SetCurrentTimeline(selectedDirector);
- }
- }
- }
- else
- {
- // the user may have changed the timeline associated with the current director
- if (state.editSequence.asset != state.editSequence.director.playableAsset)
- {
- if (!locked)
- {
- SetCurrentTimeline(state.editSequence.director);
- }
- else
- {
- // Keep locked on the current timeline but set the current director to null since it's not the timeline owner anymore
- SetCurrentTimeline(state.editSequence.asset);
- }
- }
- }
- }
- }
- void Initialize()
- {
- if (!m_HasBeenInitialized)
- {
- InitializeStateChange();
- InitializeEditorCallbacks();
- m_HasBeenInitialized = true;
- }
- }
- void RefreshLastSelectionIfRequired()
- {
- // case 1088918 - workaround for the instanceID to object cache being update during Awake.
- // This corrects any playableDirector ptrs with the correct cached version
- // This can happen when going from edit to playmode
- if (m_ForceRefreshLastSelection)
- {
- m_ForceRefreshLastSelection = false;
- RestoreLastSelection(true);
- }
- }
- void InitializeGUIIfRequired()
- {
- RefreshLastSelectionIfRequired();
- InitializeTimeArea();
- if (treeView == null && state.editSequence.asset != null)
- {
- treeView = new TimelineTreeViewGUI(this, state.editSequence.asset, position);
- }
- }
- void UpdateGUIConstants()
- {
- m_HorizontalScrollBarSize =
- GUI.skin.horizontalScrollbar.fixedHeight + GUI.skin.horizontalScrollbar.margin.top;
- m_VerticalScrollBarSize = (treeView != null && treeView.showingVerticalScrollBar)
- ? GUI.skin.verticalScrollbar.fixedWidth + GUI.skin.verticalScrollbar.margin.left
- : 0;
- }
- void UpdateViewStateHash()
- {
- if (Event.current.type == EventType.Layout)
- state.UpdateViewStateHash();
- }
- static bool PerformUndo()
- {
- if (!Event.current.isKey)
- return false;
- if (Event.current.keyCode != KeyCode.Z)
- return false;
- if (!EditorGUI.actionKey)
- return false;
- return true;
- }
- public void RebuildGraphIfNecessary(bool evaluate = true)
- {
- if (state == null || state.editSequence.director == null || state.editSequence.asset == null)
- return;
- if (state.rebuildGraph)
- {
- // rebuilding the graph resets the time
- double time = state.editSequence.time;
- var wasPlaying = false;
- // disable preview mode,
- if (!state.ignorePreview)
- {
- wasPlaying = state.playing;
- state.previewMode = false;
- state.GatherProperties(state.masterSequence.director);
- }
- state.RebuildPlayableGraph();
- state.editSequence.time = time;
- if (wasPlaying)
- state.Play();
- if (evaluate)
- {
- // put the scene back in the correct state
- state.EvaluateImmediate();
- // this is necessary to see accurate results when inspector refreshes
- // case 1154802 - this will property re-force time on the director, so
- // the play head won't snap back to the timeline duration on rebuilds
- if (!state.playing)
- state.Evaluate();
- }
- Repaint();
- }
- state.rebuildGraph = false;
- }
- // for tests
- public new void RepaintImmediately()
- {
- base.RepaintImmediately();
- }
- internal static bool IsEditingTimelineAsset(TimelineAsset timelineAsset)
- {
- return instance != null && instance.state != null && instance.state.editSequence.asset == timelineAsset;
- }
- internal static void RepaintIfEditingTimelineAsset(TimelineAsset timelineAsset)
- {
- if (IsEditingTimelineAsset(timelineAsset))
- instance.Repaint();
- }
- internal class DoCreateTimeline : ProjectWindowCallback.EndNameEditAction
- {
- public override void Action(int instanceId, string pathName, string resourceFile)
- {
- var timeline = TimelineUtility.CreateAndSaveTimelineAsset(pathName);
- ProjectWindowUtil.ShowCreatedAsset(timeline);
- }
- }
- [MenuItem("Assets/Create/Timeline", false, 450)]
- public static void CreateNewTimeline()
- {
- var icon = EditorGUIUtility.IconContent("TimelineAsset Icon").image as Texture2D;
- ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance<DoCreateTimeline>(), "New Timeline.playable", icon, null);
- }
- [MenuItem("Window/Sequencing/Timeline", false, 1)]
- public static void ShowWindow()
- {
- GetWindow<TimelineWindow>(typeof(SceneView));
- instance.Focus();
- }
- [OnOpenAsset(1)]
- public static bool OnDoubleClick(int instanceID, int line)
- {
- var assetDoubleClicked = EditorUtility.InstanceIDToObject(instanceID) as TimelineAsset;
- if (assetDoubleClicked == null)
- return false;
- ShowWindow();
- instance.SetCurrentTimeline(assetDoubleClicked);
- return true;
- }
- public virtual void AddItemsToMenu(GenericMenu menu)
- {
- bool disabled = state == null || state.editSequence.asset == null;
- m_LockTracker.AddItemsToMenu(menu, disabled);
- }
- protected virtual void ShowButton(Rect r)
- {
- bool disabled = state == null || state.editSequence.asset == null;
- m_LockTracker.ShowButton(r, DirectorStyles.Instance.timelineLockButton, disabled);
- }
- }
- }
|