#if !UNITY_2019_1_OR_NEWER #define CINEMACHINE_TIMELINE #endif #if CINEMACHINE_TIMELINE using UnityEditor; using UnityEngine; using Cinemachine.Editor; using System.Collections.Generic; using UnityEditor.Timeline; using Cinemachine; //namespace Cinemachine.Timeline //{ [CustomEditor(typeof(CinemachineShot))] internal sealed class CinemachineShotEditor : BaseEditor { static string kAutoCreateKey = "CM_Timeline_AutoCreateShotFromSceneView"; public static bool AutoCreateShotFromSceneView { get { return EditorPrefs.GetBool(kAutoCreateKey, false); } set { if (value != AutoCreateShotFromSceneView) EditorPrefs.SetBool(kAutoCreateKey, value); } } #if UNITY_2019_2_OR_NEWER static string kUseScrubbingCache = "CNMCN_Timeline_CachedScrubbing"; public static bool UseScrubbingCache { get { return EditorPrefs.GetBool(kUseScrubbingCache, false); } set { if (UseScrubbingCache != value) { EditorPrefs.SetBool(kUseScrubbingCache, value); TargetPositionCache.UseCache = value; } } } [InitializeOnLoad] public class SyncCacheEnabledSetting { static SyncCacheEnabledSetting() { TargetPositionCache.UseCache = UseScrubbingCache; } } #endif static public CinemachineVirtualCameraBase CreateStaticVcamFromSceneView() { CinemachineVirtualCameraBase vcam = CinemachineMenu.CreateStaticVirtualCamera(); vcam.m_StandbyUpdate = CinemachineVirtualCameraBase.StandbyUpdateMode.Never; #if false // GML this is too bold. What if timeline is a child of something moving? // also, SetActive(false) prevents the animator from being able to animate the object vcam.gameObject.SetActive(false); var d = TimelineEditor.inspectedDirector; if (d != null) Undo.SetTransformParent(vcam.transform, d.transform, ""); #endif return vcam; } private static readonly GUIContent kVirtualCameraLabel = new GUIContent("Virtual Camera", "The virtual camera to use for this shot"); private static readonly GUIContent kAutoCreateLabel = new GUIContent( "Auto-create new shots", "When enabled, new clips will be " + "automatically populated to match the scene view camera. " + "This is a global setting"); #if UNITY_2019_2_OR_NEWER private static readonly GUIContent kScrubbingCacheLabel = new GUIContent( "Cached Scrubbing", "For preview scrubbing, caches target positions and pre-simulates each frame to " + "approximate damping and noise playback. Target position cache is built when timeline is " + "played forward, and used when timeline is scrubbed within the indicated zone. " + "This is a global setting,."); GUIContent m_ClearText = new GUIContent("Clear", "Clear the target position scrubbing cache"); #endif /// Get the property names to exclude in the inspector. /// Add the names to this list protected override void GetExcludedPropertiesInInspector(List excluded) { base.GetExcludedPropertiesInInspector(excluded); excluded.Add(FieldPath(x => x.VirtualCamera)); } private void OnDisable() { DestroyComponentEditors(); } private void OnDestroy() { DestroyComponentEditors(); } public override void OnInspectorGUI() { BeginInspector(); SerializedProperty vcamProperty = FindProperty(x => x.VirtualCamera); EditorGUI.indentLevel = 0; // otherwise subeditor layouts get screwed up AutoCreateShotFromSceneView = EditorGUILayout.Toggle(kAutoCreateLabel, AutoCreateShotFromSceneView); Rect rect; #if UNITY_2019_2_OR_NEWER GUI.enabled = !Application.isPlaying; rect = EditorGUILayout.GetControlRect(); var r = rect; r.width = EditorGUIUtility.labelWidth + EditorGUIUtility.singleLineHeight; if (Application.isPlaying) EditorGUI.Toggle(r, kScrubbingCacheLabel, false); else UseScrubbingCache = EditorGUI.Toggle(r, kScrubbingCacheLabel, UseScrubbingCache); r.x += r.width; r.width = rect.width - r.width; var buttonWidth = GUI.skin.button.CalcSize(m_ClearText).x; r.width -= buttonWidth; EditorGUI.LabelField(r, "(experimental)"); r.x += r.width; r.width =buttonWidth; GUI.enabled &= !TargetPositionCache.IsEmpty; if (GUI.Button(r, m_ClearText)) TargetPositionCache.ClearCache(); GUI.enabled = true; #endif EditorGUILayout.Space(); CinemachineVirtualCameraBase vcam = vcamProperty.exposedReferenceValue as CinemachineVirtualCameraBase; if (vcam != null) EditorGUILayout.PropertyField(vcamProperty, kVirtualCameraLabel); else { GUIContent createLabel = new GUIContent("Create"); Vector2 createSize = GUI.skin.button.CalcSize(createLabel); rect = EditorGUILayout.GetControlRect(true); rect.width -= createSize.x; EditorGUI.PropertyField(rect, vcamProperty, kVirtualCameraLabel); rect.x += rect.width; rect.width = createSize.x; if (GUI.Button(rect, createLabel)) { vcam = CreateStaticVcamFromSceneView(); vcamProperty.exposedReferenceValue = vcam; } serializedObject.ApplyModifiedProperties(); } EditorGUI.BeginChangeCheck(); DrawRemainingPropertiesInInspector(); if (vcam != null) DrawSubeditors(vcam); // by default timeline rebuilds the entire graph when something changes, // but if a property of the virtual camera changes, we only need to re-evaluate the timeline. // this prevents flicker on post processing updates if (EditorGUI.EndChangeCheck()) { TimelineEditor.Refresh(RefreshReason.SceneNeedsUpdate); GUI.changed = false; } } void DrawSubeditors(CinemachineVirtualCameraBase vcam) { // Create an editor for each of the cinemachine virtual cam and its components GUIStyle foldoutStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold }; UpdateComponentEditors(vcam); if (m_editors != null) { foreach (UnityEditor.Editor e in m_editors) { if (e == null || e.target == null || (e.target.hideFlags & HideFlags.HideInInspector) != 0) continue; // Separator line - how do you make a thinner one? GUILayout.Box("", new GUILayoutOption[] { GUILayout.ExpandWidth(true), GUILayout.Height(1) } ); bool expanded = true; if (!s_EditorExpanded.TryGetValue(e.target.GetType(), out expanded)) expanded = true; expanded = EditorGUILayout.Foldout( expanded, e.target.GetType().Name, true, foldoutStyle); if (expanded) e.OnInspectorGUI(); s_EditorExpanded[e.target.GetType()] = expanded; } } } CinemachineVirtualCameraBase m_cachedReferenceObject; UnityEditor.Editor[] m_editors = null; static Dictionary s_EditorExpanded = new Dictionary(); void UpdateComponentEditors(CinemachineVirtualCameraBase obj) { MonoBehaviour[] components = null; if (obj != null) components = obj.gameObject.GetComponents(); int numComponents = (components == null) ? 0 : components.Length; int numEditors = (m_editors == null) ? 0 : m_editors.Length; if (m_cachedReferenceObject != obj || (numComponents + 1) != numEditors) { DestroyComponentEditors(); m_cachedReferenceObject = obj; if (obj != null) { m_editors = new UnityEditor.Editor[components.Length + 1]; CreateCachedEditor(obj.gameObject.GetComponent(), null, ref m_editors[0]); for (int i = 0; i < components.Length; ++i) CreateCachedEditor(components[i], null, ref m_editors[i + 1]); } } } void DestroyComponentEditors() { m_cachedReferenceObject = null; if (m_editors != null) { for (int i = 0; i < m_editors.Length; ++i) { if (m_editors[i] != null) UnityEngine.Object.DestroyImmediate(m_editors[i]); m_editors[i] = null; } m_editors = null; } } } //} #endif