CinemachineVirtualCameraEditor.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System;
  4. using System.Collections.Generic;
  5. using Cinemachine.Utility;
  6. using System.Reflection;
  7. using System.Linq;
  8. namespace Cinemachine.Editor
  9. {
  10. [CustomEditor(typeof(CinemachineVirtualCamera))]
  11. [CanEditMultipleObjects]
  12. internal class CinemachineVirtualCameraEditor
  13. : CinemachineVirtualCameraBaseEditor<CinemachineVirtualCamera>
  14. {
  15. VcamStageEditorPipeline m_PipelineSet = new VcamStageEditorPipeline();
  16. [MenuItem("CONTEXT/CinemachineVirtualCamera/Adopt Game View Camera Settings")]
  17. static void AdoptGameViewCameraSettings(MenuCommand command)
  18. {
  19. var vcam = command.context as CinemachineVirtualCamera;
  20. var brain = CinemachineCore.Instance.FindPotentialTargetBrain(vcam);
  21. if (brain != null)
  22. {
  23. vcam.m_Lens = brain.CurrentCameraState.Lens;
  24. vcam.transform.position = brain.transform.position;
  25. vcam.transform.rotation = brain.transform.rotation;
  26. }
  27. }
  28. [MenuItem("CONTEXT/CinemachineVirtualCamera/Adopt Scene View Camera Settings")]
  29. static void AdoptSceneViewCameraSettings(MenuCommand command)
  30. {
  31. var vcam = command.context as CinemachineVirtualCamera;
  32. vcam.m_Lens = CinemachineMenu.MatchSceneViewCamera(vcam.transform);
  33. }
  34. protected override void OnEnable()
  35. {
  36. base.OnEnable();
  37. Undo.undoRedoPerformed += ResetTargetOnUndo;
  38. m_PipelineSet.Initialize(
  39. // GetComponent
  40. (stage, result) =>
  41. {
  42. int numNullComponents = 0;
  43. foreach (var obj in targets)
  44. {
  45. var vcam = obj as CinemachineVirtualCamera;
  46. if (vcam != null)
  47. {
  48. var c = vcam.GetCinemachineComponent(stage);
  49. if (c != null)
  50. result.Add(c);
  51. else
  52. ++numNullComponents;
  53. }
  54. }
  55. return numNullComponents;
  56. },
  57. // SetComponent
  58. (stage, type) =>
  59. {
  60. Undo.SetCurrentGroupName("Cinemachine pipeline change");
  61. foreach (var obj in targets)
  62. {
  63. var vcam = obj as CinemachineVirtualCamera;
  64. Transform owner = vcam == null ? null : vcam.GetComponentOwner();
  65. if (owner == null)
  66. continue; // maybe it's a prefab
  67. var c = vcam.GetCinemachineComponent(stage);
  68. if (c != null && c.GetType() == type)
  69. continue;
  70. if (c != null)
  71. {
  72. Undo.DestroyObjectImmediate(c);
  73. vcam.InvalidateComponentPipeline();
  74. }
  75. if (type != null)
  76. {
  77. Undo.AddComponent(owner.gameObject, type);
  78. vcam.InvalidateComponentPipeline();
  79. }
  80. }
  81. });
  82. // We only look at the first target here, on purpose
  83. if (Target != null && Target.m_LockStageInInspector != null)
  84. foreach (var s in Target.m_LockStageInInspector)
  85. m_PipelineSet.SetStageIsLocked(s);
  86. #if UNITY_2021_2_OR_NEWER
  87. CinemachineSceneToolUtility.RegisterTool(typeof(FoVTool));
  88. CinemachineSceneToolUtility.RegisterTool(typeof(FarNearClipTool));
  89. #endif
  90. }
  91. protected override void OnDisable()
  92. {
  93. Undo.undoRedoPerformed -= ResetTargetOnUndo;
  94. m_PipelineSet.Shutdown();
  95. base.OnDisable();
  96. #if UNITY_2021_2_OR_NEWER
  97. CinemachineSceneToolUtility.UnregisterTool(typeof(FoVTool));
  98. CinemachineSceneToolUtility.UnregisterTool(typeof(FarNearClipTool));
  99. #endif
  100. }
  101. void OnSceneGUI()
  102. {
  103. m_PipelineSet.OnSceneGUI(); // call hidden editors' OnSceneGUI
  104. #if UNITY_2021_2_OR_NEWER
  105. DrawSceneTools();
  106. #endif
  107. }
  108. #if UNITY_2021_2_OR_NEWER
  109. void DrawSceneTools()
  110. {
  111. var vcam = Target;
  112. if (vcam == null || !vcam.IsValid || vcam.m_ExcludedPropertiesInInspector.Contains("m_Lens"))
  113. {
  114. return;
  115. }
  116. var originalColor = Handles.color;
  117. Handles.color = Handles.preselectionColor;
  118. if (CinemachineSceneToolUtility.IsToolActive(typeof(FoVTool)))
  119. {
  120. CinemachineSceneToolHelpers.FovToolHandle(vcam,
  121. new SerializedObject(vcam).FindProperty(() => vcam.m_Lens),
  122. vcam.m_Lens, IsHorizontalFOVUsed());
  123. }
  124. else if (CinemachineSceneToolUtility.IsToolActive(typeof(FarNearClipTool)))
  125. {
  126. CinemachineSceneToolHelpers.NearFarClipHandle(vcam,
  127. new SerializedObject(vcam).FindProperty(() => vcam.m_Lens));
  128. }
  129. Handles.color = originalColor;
  130. }
  131. #endif
  132. public override void OnInspectorGUI()
  133. {
  134. if (Target.GetComponentOwner() == null)
  135. {
  136. EditorGUILayout.HelpBox(
  137. "It's not possible to add this component to a prefab instance. "
  138. + "Instead, you can open the Prefab in Prefab Mode or unpack "
  139. + "the Prefab instance to remove the Prefab connection.",
  140. MessageType.Error);
  141. return;
  142. }
  143. BeginInspector();
  144. DrawHeaderInInspector();
  145. DrawPropertyInInspector(FindProperty(x => x.m_Priority));
  146. DrawTargetsInInspector(FindProperty(x => x.m_Follow), FindProperty(x => x.m_LookAt));
  147. DrawPropertyInInspector(FindProperty(x => x.m_StandbyUpdate));
  148. DrawLensSettingsInInspector(FindProperty(x => x.m_Lens));
  149. DrawRemainingPropertiesInInspector();
  150. m_PipelineSet.OnInspectorGUI(!IsPropertyExcluded("Header"));
  151. DrawExtensionsWidgetInInspector();
  152. }
  153. void ResetTargetOnUndo()
  154. {
  155. ResetTarget();
  156. for (int i = 0; i < targets.Length; ++i)
  157. (targets[i] as CinemachineVirtualCamera).InvalidateComponentPipeline();
  158. }
  159. /// <summary>
  160. /// Register with CinemachineVirtualCamera to create the pipeline in an undo-friendly manner
  161. /// </summary>
  162. [InitializeOnLoad]
  163. class CreatePipelineWithUndo
  164. {
  165. static CreatePipelineWithUndo()
  166. {
  167. CinemachineVirtualCamera.CreatePipelineOverride =
  168. (CinemachineVirtualCamera vcam, string name, CinemachineComponentBase[] copyFrom) =>
  169. {
  170. // Recycle existing pipeline child (if any)
  171. GameObject go = null;
  172. foreach (Transform child in vcam.transform)
  173. {
  174. if (child.GetComponent<CinemachinePipeline>() != null)
  175. {
  176. go = child.gameObject;
  177. break;
  178. }
  179. }
  180. if (go == null)
  181. {
  182. // Create a new pipeline - can't do it if prefab instance
  183. if (PrefabUtility.IsPartOfAnyPrefab(vcam.gameObject))
  184. return null;
  185. go = ObjectFactory.CreateGameObject(name);
  186. Undo.RegisterCreatedObjectUndo(go, "created pipeline");
  187. Undo.SetTransformParent(go.transform, vcam.transform, "parenting pipeline");
  188. Undo.AddComponent<CinemachinePipeline>(go);
  189. }
  190. var oldStuff = go.GetComponents<CinemachineComponentBase>();
  191. foreach (var c in oldStuff)
  192. Undo.DestroyObjectImmediate(c);
  193. // If copying, transfer the components
  194. if (copyFrom != null)
  195. {
  196. foreach (Component c in copyFrom)
  197. {
  198. Component copy = Undo.AddComponent(go, c.GetType());
  199. Undo.RecordObject(copy, "copying pipeline");
  200. ReflectionHelpers.CopyFields(c, copy);
  201. }
  202. }
  203. return go.transform;
  204. };
  205. CinemachineVirtualCamera.DestroyPipelineOverride = (GameObject pipeline) =>
  206. {
  207. var oldStuff = pipeline.GetComponents<CinemachineComponentBase>();
  208. foreach (var c in oldStuff)
  209. Undo.DestroyObjectImmediate(c);
  210. // Cannot create or destroy child objects if prefab.
  211. // Just leave it there in that case, it will get discovered and recycled.
  212. if (!PrefabUtility.IsPartOfAnyPrefab(pipeline))
  213. Undo.DestroyObjectImmediate(pipeline);
  214. };
  215. }
  216. }
  217. // Because the cinemachine components are attached to hidden objects, their
  218. // gizmos don't get drawn by default. We have to do it explicitly.
  219. [InitializeOnLoad]
  220. static class CollectGizmoDrawers
  221. {
  222. static CollectGizmoDrawers()
  223. {
  224. m_GizmoDrawers = new Dictionary<Type, MethodInfo>();
  225. string definedIn = typeof(CinemachineComponentBase).Assembly.GetName().Name;
  226. Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
  227. foreach (Assembly assembly in assemblies)
  228. {
  229. // Note that we have to call GetName().Name. Just GetName() will not work.
  230. if ((!assembly.GlobalAssemblyCache)
  231. && ((assembly.GetName().Name == definedIn)
  232. || assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn)))
  233. {
  234. try
  235. {
  236. foreach (var type in assembly.GetTypes())
  237. {
  238. try
  239. {
  240. bool added = false;
  241. foreach (var method in type.GetMethods(
  242. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
  243. {
  244. if (added)
  245. break;
  246. if (!method.IsStatic)
  247. continue;
  248. var attributes = method.GetCustomAttributes(typeof(DrawGizmo), true) as DrawGizmo[];
  249. foreach (var a in attributes)
  250. {
  251. if (typeof(CinemachineComponentBase).IsAssignableFrom(a.drawnType) && !a.drawnType.IsAbstract)
  252. {
  253. m_GizmoDrawers.Add(a.drawnType, method);
  254. added = true;
  255. break;
  256. }
  257. }
  258. }
  259. }
  260. catch (Exception) {} // Just skip uncooperative types
  261. }
  262. }
  263. catch (Exception) {} // Just skip uncooperative assemblies
  264. }
  265. }
  266. }
  267. public static Dictionary<Type, MethodInfo> m_GizmoDrawers;
  268. }
  269. [DrawGizmo(GizmoType.Active | GizmoType.InSelectionHierarchy, typeof(CinemachineVirtualCamera))]
  270. internal static void DrawVirtualCameraGizmos(CinemachineVirtualCamera vcam, GizmoType selectionType)
  271. {
  272. var pipeline = vcam.GetComponentPipeline();
  273. if (pipeline != null)
  274. {
  275. foreach (var c in pipeline)
  276. {
  277. if (c == null)
  278. continue;
  279. MethodInfo method;
  280. if (CollectGizmoDrawers.m_GizmoDrawers.TryGetValue(c.GetType(), out method))
  281. {
  282. if (method != null)
  283. method.Invoke(null, new object[] {c, selectionType});
  284. }
  285. }
  286. }
  287. }
  288. }
  289. }