CinemachineVirtualCameraBaseEditor.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System;
  4. using System.Collections.Generic;
  5. using Cinemachine.Utility;
  6. namespace Cinemachine.Editor
  7. {
  8. /// <summary>
  9. /// Base class for virtual camera editors.
  10. /// Handles drawing the header and the basic properties.
  11. /// </summary>
  12. /// <typeparam name="T">The type of CinemachineVirtualCameraBase being edited</typeparam>
  13. public class CinemachineVirtualCameraBaseEditor<T>
  14. : BaseEditor<T> where T : CinemachineVirtualCameraBase
  15. {
  16. /// <summary>A collection of GUIContent for use in the inspector</summary>
  17. public static class Styles
  18. {
  19. /// <summary>GUIContent for Add Extension</summary>
  20. public static GUIContent addExtensionLabel = new GUIContent("Add Extension");
  21. /// <summary>GUIContent for no-multi-select message</summary>
  22. public static GUIContent virtualCameraChildrenInfoMsg
  23. = new GUIContent("The Virtual Camera Children field is not available when multiple objects are selected.");
  24. }
  25. static Type[] sExtensionTypes; // First entry is null
  26. static string[] sExtensionNames;
  27. bool IsPrefabBase { get; set; }
  28. /// <summary>Obsolete, do not use. Use the overload, which is more performant</summary>
  29. /// <returns>List of property names to exclude</returns>
  30. protected override List<string> GetExcludedPropertiesInInspector()
  31. { return base.GetExcludedPropertiesInInspector(); }
  32. /// <summary>Get the property names to exclude in the inspector.</summary>
  33. /// <param name="excluded">Add the names to this list</param>
  34. protected override void GetExcludedPropertiesInInspector(List<string> excluded)
  35. {
  36. base.GetExcludedPropertiesInInspector(excluded);
  37. if (Target.m_ExcludedPropertiesInInspector != null)
  38. excluded.AddRange(Target.m_ExcludedPropertiesInInspector);
  39. }
  40. /// <summary>Inspector panel is being enabled</summary>
  41. protected virtual void OnEnable()
  42. {
  43. IsPrefabBase = Target.gameObject.scene.name == null; // causes a small GC alloc
  44. if (sExtensionTypes == null)
  45. {
  46. // Populate the extension list
  47. List<Type> exts = new List<Type>();
  48. List<string> names = new List<string>();
  49. exts.Add(null);
  50. names.Add("(select)");
  51. var allExtensions
  52. = ReflectionHelpers.GetTypesInAllDependentAssemblies(
  53. (Type t) => typeof(CinemachineExtension).IsAssignableFrom(t) && !t.IsAbstract);
  54. foreach (Type t in allExtensions)
  55. {
  56. exts.Add(t);
  57. names.Add(t.Name);
  58. }
  59. sExtensionTypes = exts.ToArray();
  60. sExtensionNames = names.ToArray();
  61. }
  62. }
  63. /// <summary>Inspector panel is being disabled</summary>
  64. protected virtual void OnDisable()
  65. {
  66. if (CinemachineBrain.SoloCamera == (ICinemachineCamera)Target)
  67. {
  68. CinemachineBrain.SoloCamera = null;
  69. InspectorUtility.RepaintGameView();
  70. }
  71. }
  72. /// <summary>Create the contents of the inspector panel</summary>
  73. public override void OnInspectorGUI()
  74. {
  75. BeginInspector();
  76. DrawHeaderInInspector();
  77. DrawRemainingPropertiesInInspector();
  78. DrawExtensionsWidgetInInspector();
  79. }
  80. /// <summary>
  81. /// Draw the virtual camera header in the inspector.
  82. /// This includes Solo button, Live status, and global settings
  83. /// </summary>
  84. protected void DrawHeaderInInspector()
  85. {
  86. if (!IsPropertyExcluded("Header"))
  87. {
  88. DrawCameraStatusInInspector();
  89. DrawGlobalControlsInInspector();
  90. }
  91. ExcludeProperty("Header");
  92. }
  93. /// <summary>
  94. /// Draw the LookAt and Follow targets in the inspector
  95. /// </summary>
  96. /// <param name="followTarget">Follow target property</param>
  97. /// <param name="lookAtTarget">LookAt target property</param>
  98. protected void DrawTargetsInInspector(
  99. SerializedProperty followTarget, SerializedProperty lookAtTarget)
  100. {
  101. EditorGUI.BeginChangeCheck();
  102. if (!IsPropertyExcluded(followTarget.name))
  103. {
  104. if (Target.ParentCamera == null || Target.ParentCamera.Follow == null)
  105. EditorGUILayout.PropertyField(followTarget);
  106. else
  107. EditorGUILayout.PropertyField(followTarget,
  108. new GUIContent(followTarget.displayName + " Override"));
  109. ExcludeProperty(followTarget.name);
  110. }
  111. if (!IsPropertyExcluded(lookAtTarget.name))
  112. {
  113. if (Target.ParentCamera == null || Target.ParentCamera.LookAt == null)
  114. EditorGUILayout.PropertyField(lookAtTarget);
  115. else
  116. EditorGUILayout.PropertyField(lookAtTarget,
  117. new GUIContent(lookAtTarget.displayName + " Override"));
  118. ExcludeProperty(lookAtTarget.name);
  119. }
  120. if (EditorGUI.EndChangeCheck())
  121. serializedObject.ApplyModifiedProperties();
  122. }
  123. /// <summary>
  124. /// Draw the Extensions dropdown in the inspector
  125. /// </summary>
  126. protected void DrawExtensionsWidgetInInspector()
  127. {
  128. if (!IsPropertyExcluded("Extensions"))
  129. {
  130. EditorGUILayout.Space();
  131. EditorGUILayout.LabelField("Extensions", EditorStyles.boldLabel);
  132. Rect rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
  133. rect = EditorGUI.PrefixLabel(rect, Styles.addExtensionLabel);
  134. int selection = EditorGUI.Popup(rect, 0, sExtensionNames);
  135. if (selection > 0)
  136. {
  137. Type extType = sExtensionTypes[selection];
  138. for (int i = 0; i < targets.Length; i++)
  139. {
  140. var targetGO = (targets[i] as CinemachineVirtualCameraBase).gameObject;
  141. if (targetGO != null && targetGO.GetComponent(extType) == null)
  142. Undo.AddComponent(targetGO, extType);
  143. }
  144. }
  145. ExcludeProperty("Extensions");
  146. }
  147. }
  148. /// <summary>
  149. /// Draw the Live status in the inspector, and the Solo button
  150. /// </summary>
  151. protected void DrawCameraStatusInInspector()
  152. {
  153. if (Selection.objects.Length > 1)
  154. return;
  155. // Is the camera navel-gazing?
  156. CameraState state = Target.State;
  157. if (state.HasLookAt && (state.ReferenceLookAt - state.CorrectedPosition).AlmostZero())
  158. EditorGUILayout.HelpBox(
  159. "The camera is positioned on the same point at which it is trying to look.",
  160. MessageType.Warning);
  161. // No status and Solo for prefabs
  162. if (IsPrefabBase)
  163. return;
  164. // Active status and Solo button
  165. Rect rect = EditorGUILayout.GetControlRect(true);
  166. Rect rectLabel = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth, rect.height);
  167. rect.width -= rectLabel.width;
  168. rect.x += rectLabel.width;
  169. Color color = GUI.color;
  170. bool isSolo = (CinemachineBrain.SoloCamera == (ICinemachineCamera)Target);
  171. if (isSolo)
  172. GUI.color = CinemachineBrain.GetSoloGUIColor();
  173. bool isLive = CinemachineCore.Instance.IsLive(Target);
  174. GUI.enabled = isLive;
  175. GUI.Label(rectLabel, isLive ? "Status: Live"
  176. : (Target.isActiveAndEnabled ? "Status: Standby" : "Status: Disabled"));
  177. GUI.enabled = true;
  178. float labelWidth = 0;
  179. GUIContent updateText = GUIContent.none;
  180. UpdateTracker.UpdateClock updateMode = CinemachineCore.Instance.GetVcamUpdateStatus(Target);
  181. if (Application.isPlaying)
  182. {
  183. updateText = new GUIContent(
  184. updateMode == UpdateTracker.UpdateClock.Fixed ? " Fixed Update" : " Late Update");
  185. var textDimensions = GUI.skin.label.CalcSize(updateText);
  186. labelWidth = textDimensions.x;
  187. }
  188. rect.width -= labelWidth;
  189. if (GUI.Button(rect, "Solo", "Button"))
  190. {
  191. isSolo = !isSolo;
  192. CinemachineBrain.SoloCamera = isSolo ? Target : null;
  193. InspectorUtility.RepaintGameView();
  194. }
  195. GUI.color = color;
  196. if (isSolo && !Application.isPlaying)
  197. InspectorUtility.RepaintGameView();
  198. if (labelWidth > 0)
  199. {
  200. GUI.enabled = false;
  201. rect.x += rect.width; rect.width = labelWidth;
  202. GUI.Label(rect, updateText);
  203. GUI.enabled = true;
  204. }
  205. }
  206. /// <summary>
  207. /// Draw the global settings controls in the inspector
  208. /// </summary>
  209. protected void DrawGlobalControlsInInspector()
  210. {
  211. CinemachineSettings.CinemachineCoreSettings.ShowInGameGuides
  212. = EditorGUILayout.Toggle(
  213. new GUIContent(
  214. "Game Window Guides",
  215. "Enable the display of overlays in the Game window. You can adjust colours and opacity in Edit/Preferences/Cinemachine."),
  216. CinemachineSettings.CinemachineCoreSettings.ShowInGameGuides);
  217. SaveDuringPlay.SaveDuringPlay.Enabled
  218. = EditorGUILayout.Toggle(
  219. new GUIContent(
  220. "Save During Play",
  221. "If checked, Virtual Camera settings changes made during Play Mode will be propagated back to the scene when Play Mode is exited."),
  222. SaveDuringPlay.SaveDuringPlay.Enabled);
  223. if (Application.isPlaying && SaveDuringPlay.SaveDuringPlay.Enabled)
  224. EditorGUILayout.HelpBox(
  225. " Virtual Camera settings changes made during Play Mode will be propagated back to the scene when Play Mode is exited.",
  226. MessageType.Info);
  227. }
  228. }
  229. }