#if !CINEMACHINE_NO_CM2_SUPPORT using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; #if CINEMACHINE_UNITY_INPUTSYSTEM using UnityEngine.InputSystem; #endif namespace Unity.Cinemachine.Editor { /// /// Base class for virtual camera editors. /// Handles drawing the header and the basic properties. /// /// The type of CinemachineVirtualCameraBase being edited [Obsolete] class CinemachineVirtualCameraBaseEditor : BaseEditor where T : CinemachineVirtualCameraBase { static GUIContent s_AddExtensionLabel = new ("Add Extension", "Add a Cinemachine Extension behaviour"); static Type[] sExtensionTypes; // First entry is null static string[] sExtensionNames; bool IsPrefabBase { get; set; } /// Update state information on undo/redo void UpdateCameraState() { if (Target != null) Target.InternalUpdateCameraState(Vector3.up, -1); } /// Inspector panel is being enabled. /// Implementation should call the base class implementation protected virtual void OnEnable() { Undo.undoRedoPerformed += UpdateCameraState; IsPrefabBase = Target.gameObject.scene.name == null; // causes a small GC alloc if (sExtensionTypes == null) { // Populate the extension list List exts = new List(); List names = new List(); exts.Add(null); names.Add("(select)"); var allExtensions = ReflectionHelpers.GetTypesInAllDependentAssemblies( (Type t) => typeof(CinemachineExtension).IsAssignableFrom(t) && !t.IsAbstract && t.GetCustomAttribute() == null); foreach (Type t in allExtensions) { exts.Add(t); names.Add(t.Name); } sExtensionTypes = exts.ToArray(); sExtensionNames = names.ToArray(); } } /// Inspector panel is being disabled. /// Implementation should call the base class implementation protected virtual void OnDisable() { Undo.undoRedoPerformed -= UpdateCameraState; if (CinemachineCore.SoloCamera == Target) { CinemachineCore.SoloCamera = null; InspectorUtility.RepaintGameView(); } } protected virtual void DrawStandardInspectorTopSection() { DrawCameraStatusInInspector(); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(serializedObject.FindProperty(() => Target.StandbyUpdate)); EditorGUILayout.PropertyField(serializedObject.FindProperty(() => Target.Priority)); EditorGUILayout.PropertyField(serializedObject.FindProperty(() => Target.OutputChannel)); if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties(); DrawGlobalControlsInInspector(); } #if CINEMACHINE_UNITY_INPUTSYSTEM /// /// Draw a message prompting the user to add a CinemachineInputProvider. /// Does nothing if Input package not installed. /// protected void DrawInputProviderButtonInInspector() { bool needsButton = false; for (int i = 0; !needsButton && i < targets.Length; ++i) { var vcam = (CinemachineVirtualCameraBase)targets[i]; var requirer = vcam as AxisState.IRequiresInput; if (requirer != null && requirer.RequiresInput() && !vcam.TryGetComponent(out _)) needsButton = true; } if (!needsButton) return; EditorGUILayout.Space(); InspectorUtility.HelpBoxWithButton( "The InputSystem package is installed, but it is not used to control this vcam.", MessageType.Info, new GUIContent("Add Input\nProvider"), () => { Undo.SetCurrentGroupName("Add CinemachineInputProvider"); for (int i = 0; i < targets.Length; ++i) { var vcam = (CinemachineVirtualCameraBase)targets[i]; if (vcam.TryGetComponent(out var _)) continue; var inputProvider = Undo.AddComponent(vcam.gameObject); inputProvider.XYAxis = (InputActionReference)AssetDatabase.LoadAllAssetsAtPath( "Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.inputactions").FirstOrDefault( x => x.name == "Player/Look"); } }); EditorGUILayout.Space(); } #else /// /// Draw a message prompting the user to add a CinemachineInputProvider. /// Does nothing if Input package not installed. /// protected void DrawInputProviderButtonInInspector() {} #endif /// /// Draw the LookAt and Follow targets in the inspector /// /// Follow target SerializedProperty /// LookAt target SerializedProperty protected void DrawTargetsInInspector( SerializedProperty followTarget, SerializedProperty lookAtTarget) { EditorGUI.BeginChangeCheck(); var parentVcam = Target.ParentCamera as CinemachineVirtualCameraBase; if (!IsPropertyExcluded(followTarget.name)) { if (parentVcam == null || parentVcam.Follow == null) EditorGUILayout.PropertyField(followTarget); else EditorGUILayout.PropertyField(followTarget, new GUIContent(followTarget.displayName + " Override")); ExcludeProperty(followTarget.name); } if (!IsPropertyExcluded(lookAtTarget.name)) { if (parentVcam == null || parentVcam.LookAt == null) EditorGUILayout.PropertyField(lookAtTarget); else EditorGUILayout.PropertyField(lookAtTarget, new GUIContent(lookAtTarget.displayName + " Override")); ExcludeProperty(lookAtTarget.name); } if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties(); } /// /// Draw the Extensions dropdown in the inspector /// protected void DrawExtensionsWidgetInInspector() { EditorGUILayout.Space(); EditorGUILayout.LabelField("Extensions", EditorStyles.boldLabel); Rect rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); rect = EditorGUI.PrefixLabel(rect, s_AddExtensionLabel); int selection = EditorGUI.Popup(rect, 0, sExtensionNames); if (selection > 0) { Type extType = sExtensionTypes[selection]; for (int i = 0; i < targets.Length; i++) { var targetGO = (targets[i] as CinemachineVirtualCameraBase).gameObject; if (targetGO != null && targetGO.GetComponent(extType) == null) Undo.AddComponent(targetGO, extType); } } } /// /// Draw the Live status in the inspector, and the Solo button /// protected void DrawCameraStatusInInspector() { if (Selection.objects.Length > 1) return; // Is the camera navel-gazing? CameraState state = Target.State; if (state.HasLookAt() && (state.ReferenceLookAt - state.GetCorrectedPosition()).AlmostZero()) EditorGUILayout.HelpBox( "The camera is positioned on the same point at which it is trying to look.", MessageType.Warning); // No status and Solo for prefabs if (IsPrefabBase) return; // Active status and Solo button Rect rect = EditorGUILayout.GetControlRect(true); Rect rectLabel = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth, rect.height); rect.width -= rectLabel.width; rect.x += rectLabel.width; Color color = GUI.color; bool isSolo = (CinemachineCore.SoloCamera == Target); if (isSolo) GUI.color = CinemachineCore.SoloGUIColor(); bool isLive = CinemachineCore.IsLive(Target); GUI.enabled = isLive; GUI.Label(rectLabel, isLive ? "Status: Live" : (Target.isActiveAndEnabled ? "Status: Standby" : "Status: Disabled")); GUI.enabled = true; float labelWidth = 0; GUIContent updateText = GUIContent.none; UpdateTracker.UpdateClock updateMode = CameraUpdateManager.GetVcamUpdateStatus(Target); if (Application.isPlaying) { updateText = new GUIContent( updateMode == UpdateTracker.UpdateClock.Fixed ? " Fixed Update" : " Late Update"); var textDimensions = GUI.skin.label.CalcSize(updateText); labelWidth = textDimensions.x; } rect.width -= labelWidth; if (GUI.Button(rect, "Solo", "Button")) { isSolo = !isSolo; CinemachineCore.SoloCamera = isSolo ? Target : null; InspectorUtility.RepaintGameView(); } GUI.color = color; if (isSolo && !Application.isPlaying) InspectorUtility.RepaintGameView(); if (labelWidth > 0) { GUI.enabled = false; rect.x += rect.width; rect.width = labelWidth; GUI.Label(rect, updateText); GUI.enabled = true; } } GUIContent m_GuidesLabel; static GUIContent[] s_GuidesChoices = new [] { new GUIContent("Disabled"), new GUIContent("Passive"), new GUIContent("Interactive") }; /// /// Draw the global settings controls in the inspector /// protected void DrawGlobalControlsInInspector() { if (m_GuidesLabel == null) m_GuidesLabel = new ("Game View Guides", CinemachineCorePrefs.s_ShowInGameGuidesLabel.tooltip); SaveDuringPlay.Enabled = EditorGUILayout.Toggle( CinemachineCorePrefs.s_SaveDuringPlayLabel, SaveDuringPlay.Enabled); int index = CinemachineCorePrefs.ShowInGameGuides.Value ? (CinemachineCorePrefs.DraggableComposerGuides.Value ? 2 : 1) : 0; var newIndex = EditorGUILayout.Popup(m_GuidesLabel, index, s_GuidesChoices); if (index != newIndex) { CinemachineCorePrefs.ShowInGameGuides.Value = newIndex != 0; CinemachineCorePrefs.DraggableComposerGuides.Value = newIndex == 2; InspectorUtility.RepaintGameView(); } if (Application.isPlaying && SaveDuringPlay.Enabled) EditorGUILayout.HelpBox( "CinemachineCamera settings changes made during Play Mode will be " + "propagated back to the scene when Play Mode is exited.", MessageType.Info); } internal bool IsHorizontalFOVUsed() { // This should be a global UX setting, but the best we can do now is to query // the associated camera (if any) for the lens FOV display mode Camera camera = null; var brain = CinemachineCore.FindPotentialTargetBrain(Target); if (brain != null) camera = brain.OutputCamera; if (camera != null) { var p = new SerializedObject(camera).FindProperty("m_FOVAxisMode"); if (p != null && p.intValue == (int)Camera.FieldOfViewAxis.Horizontal) return true; } return false; } } } #endif