using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; using Cinemachine.Utility; namespace Cinemachine.Editor { /// /// Base class for virtual camera editors. /// Handles drawing the header and the basic properties. /// /// The type of CinemachineVirtualCameraBase being edited public class CinemachineVirtualCameraBaseEditor : BaseEditor where T : CinemachineVirtualCameraBase { /// A collection of GUIContent for use in the inspector public static class Styles { /// GUIContent for Add Extension public static GUIContent addExtensionLabel = new GUIContent("Add Extension"); /// GUIContent for no-multi-select message public static GUIContent virtualCameraChildrenInfoMsg = new GUIContent("The Virtual Camera Children field is not available when multiple objects are selected."); } static Type[] sExtensionTypes; // First entry is null static string[] sExtensionNames; bool IsPrefabBase { get; set; } /// Obsolete, do not use. Use the overload, which is more performant /// List of property names to exclude protected override List GetExcludedPropertiesInInspector() { return base.GetExcludedPropertiesInInspector(); } /// Get the property names to exclude in the inspector. /// Add the names to this list protected override void GetExcludedPropertiesInInspector(List excluded) { base.GetExcludedPropertiesInInspector(excluded); if (Target.m_ExcludedPropertiesInInspector != null) excluded.AddRange(Target.m_ExcludedPropertiesInInspector); } /// Inspector panel is being enabled protected virtual void OnEnable() { 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); foreach (Type t in allExtensions) { exts.Add(t); names.Add(t.Name); } sExtensionTypes = exts.ToArray(); sExtensionNames = names.ToArray(); } } /// Inspector panel is being disabled protected virtual void OnDisable() { if (CinemachineBrain.SoloCamera == (ICinemachineCamera)Target) { CinemachineBrain.SoloCamera = null; InspectorUtility.RepaintGameView(); } } /// Create the contents of the inspector panel public override void OnInspectorGUI() { BeginInspector(); DrawHeaderInInspector(); DrawRemainingPropertiesInInspector(); DrawExtensionsWidgetInInspector(); } /// /// Draw the virtual camera header in the inspector. /// This includes Solo button, Live status, and global settings /// protected void DrawHeaderInInspector() { if (!IsPropertyExcluded("Header")) { DrawCameraStatusInInspector(); DrawGlobalControlsInInspector(); } ExcludeProperty("Header"); } /// /// Draw the LookAt and Follow targets in the inspector /// /// Follow target property /// LookAt target property protected void DrawTargetsInInspector( SerializedProperty followTarget, SerializedProperty lookAtTarget) { EditorGUI.BeginChangeCheck(); if (!IsPropertyExcluded(followTarget.name)) { if (Target.ParentCamera == null || Target.ParentCamera.Follow == null) EditorGUILayout.PropertyField(followTarget); else EditorGUILayout.PropertyField(followTarget, new GUIContent(followTarget.displayName + " Override")); ExcludeProperty(followTarget.name); } if (!IsPropertyExcluded(lookAtTarget.name)) { if (Target.ParentCamera == null || Target.ParentCamera.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() { if (!IsPropertyExcluded("Extensions")) { EditorGUILayout.Space(); EditorGUILayout.LabelField("Extensions", EditorStyles.boldLabel); Rect rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); rect = EditorGUI.PrefixLabel(rect, Styles.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); } } ExcludeProperty("Extensions"); } } /// /// 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.CorrectedPosition).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 = (CinemachineBrain.SoloCamera == (ICinemachineCamera)Target); if (isSolo) GUI.color = CinemachineBrain.GetSoloGUIColor(); bool isLive = CinemachineCore.Instance.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 = CinemachineCore.Instance.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; CinemachineBrain.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; } } /// /// Draw the global settings controls in the inspector /// protected void DrawGlobalControlsInInspector() { CinemachineSettings.CinemachineCoreSettings.ShowInGameGuides = EditorGUILayout.Toggle( new GUIContent( "Game Window Guides", "Enable the display of overlays in the Game window. You can adjust colours and opacity in Edit/Preferences/Cinemachine."), CinemachineSettings.CinemachineCoreSettings.ShowInGameGuides); SaveDuringPlay.SaveDuringPlay.Enabled = EditorGUILayout.Toggle( new GUIContent( "Save During Play", "If checked, Virtual Camera settings changes made during Play Mode will be propagated back to the scene when Play Mode is exited."), SaveDuringPlay.SaveDuringPlay.Enabled); if (Application.isPlaying && SaveDuringPlay.SaveDuringPlay.Enabled) EditorGUILayout.HelpBox( " Virtual Camera settings changes made during Play Mode will be propagated back to the scene when Play Mode is exited.", MessageType.Info); } } }