CinemachineFreeLookEditor.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. using UnityEngine;
  2. using UnityEditor;
  3. using Cinemachine.Editor;
  4. using System.Collections.Generic;
  5. using Cinemachine.Utility;
  6. using Matrix4x4 = UnityEngine.Matrix4x4;
  7. using Quaternion = UnityEngine.Quaternion;
  8. using Vector3 = UnityEngine.Vector3;
  9. namespace Cinemachine
  10. {
  11. [CustomEditor(typeof(CinemachineFreeLook))]
  12. [CanEditMultipleObjects]
  13. internal sealed class CinemachineFreeLookEditor
  14. : CinemachineVirtualCameraBaseEditor<CinemachineFreeLook>
  15. {
  16. /// <summary>Get the property names to exclude in the inspector.</summary>
  17. /// <param name="excluded">Add the names to this list</param>
  18. protected override void GetExcludedPropertiesInInspector(List<string> excluded)
  19. {
  20. base.GetExcludedPropertiesInInspector(excluded);
  21. excluded.Add(FieldPath(x => x.m_Orbits));
  22. if (!Target.m_CommonLens)
  23. excluded.Add(FieldPath(x => x.m_Lens));
  24. if (Target.m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
  25. {
  26. excluded.Add(FieldPath(x => x.m_Heading));
  27. excluded.Add(FieldPath(x => x.m_RecenterToTargetHeading));
  28. }
  29. }
  30. protected override void OnEnable()
  31. {
  32. base.OnEnable();
  33. Target.UpdateInputAxisProvider();
  34. #if UNITY_2021_2_OR_NEWER
  35. CinemachineSceneToolUtility.RegisterTool(typeof(FoVTool));
  36. CinemachineSceneToolUtility.RegisterTool(typeof(FarNearClipTool));
  37. CinemachineSceneToolUtility.RegisterTool(typeof(FollowOffsetTool));
  38. //CinemachineSceneToolUtility.RegisterTool(typeof(FreelookRigSelection));
  39. #endif
  40. }
  41. protected override void OnDisable()
  42. {
  43. base.OnDisable();
  44. // Must destroy child editors or we get exceptions
  45. if (m_rigEditor != null)
  46. UnityEngine.Object.DestroyImmediate(m_rigEditor);
  47. #if UNITY_2021_2_OR_NEWER
  48. CinemachineSceneToolUtility.UnregisterTool(typeof(FoVTool));
  49. CinemachineSceneToolUtility.UnregisterTool(typeof(FarNearClipTool));
  50. CinemachineSceneToolUtility.UnregisterTool(typeof(FollowOffsetTool));
  51. //CinemachineSceneToolUtility.UnregisterTool(typeof(FreelookRigSelection));
  52. #endif
  53. }
  54. public override void OnInspectorGUI()
  55. {
  56. Target.GetRig(0); // force an update of the rig cache
  57. if (!Target.RigsAreCreated)
  58. {
  59. EditorGUILayout.HelpBox(
  60. "It's not possible to add this component to a prefab instance. "
  61. + "Instead, you can open the Prefab in Prefab Mode or unpack "
  62. + "the Prefab instance to remove the Prefab connection.",
  63. MessageType.Error);
  64. return;
  65. }
  66. Target.m_XAxis.ValueRangeLocked
  67. = (Target.m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp);
  68. // Ordinary properties
  69. BeginInspector();
  70. DrawHeaderInInspector();
  71. DrawPropertyInInspector(FindProperty(x => x.m_Priority));
  72. DrawTargetsInInspector(FindProperty(x => x.m_Follow), FindProperty(x => x.m_LookAt));
  73. DrawPropertyInInspector(FindProperty(x => x.m_StandbyUpdate));
  74. DrawPropertyInInspector(FindProperty(x => x.m_CommonLens));
  75. DrawLensSettingsInInspector(FindProperty(x => x.m_Lens));
  76. DrawRemainingPropertiesInInspector();
  77. // Orbits
  78. EditorGUI.BeginChangeCheck();
  79. SerializedProperty orbits = FindProperty(x => x.m_Orbits);
  80. for (int i = 0; i < CinemachineFreeLook.RigNames.Length; ++i)
  81. {
  82. Rect rect = EditorGUILayout.GetControlRect(true);
  83. SerializedProperty orbit = orbits.GetArrayElementAtIndex(i);
  84. InspectorUtility.MultiPropertyOnLine(rect,
  85. new GUIContent(CinemachineFreeLook.RigNames[i]),
  86. new [] { orbit.FindPropertyRelative(() => Target.m_Orbits[i].m_Height),
  87. orbit.FindPropertyRelative(() => Target.m_Orbits[i].m_Radius) },
  88. null);
  89. }
  90. if (EditorGUI.EndChangeCheck())
  91. serializedObject.ApplyModifiedProperties();
  92. // Rigs
  93. if (Selection.objects.Length == 1)
  94. {
  95. EditorGUILayout.Separator();
  96. EditorGUILayout.Separator();
  97. SetSelectedRig(Target, GUILayout.Toolbar(GetSelectedRig(Target), s_RigNames));
  98. UpdateRigEditor();
  99. if (m_rigEditor != null)
  100. {
  101. EditorGUILayout.BeginVertical(GUI.skin.box);
  102. ++EditorGUI.indentLevel;
  103. m_rigEditor.OnInspectorGUI();
  104. --EditorGUI.indentLevel;
  105. EditorGUILayout.EndVertical();
  106. }
  107. }
  108. // Extensions
  109. DrawExtensionsWidgetInInspector();
  110. }
  111. void OnSceneGUI()
  112. {
  113. // Forward to embedded rig editor
  114. if (m_rigEditor != null && m_RigEditorOnSceneGUI != null)
  115. m_RigEditorOnSceneGUI.Invoke(m_rigEditor, null);
  116. #if UNITY_2021_2_OR_NEWER
  117. DrawSceneTools();
  118. #endif
  119. }
  120. #if UNITY_2021_2_OR_NEWER
  121. void DrawSceneTools()
  122. {
  123. var freelook = Target;
  124. if (freelook == null || !freelook.IsValid)
  125. {
  126. return;
  127. }
  128. var originalColor = Handles.color;
  129. Handles.color = Handles.preselectionColor;
  130. if (freelook.m_CommonLens && CinemachineSceneToolUtility.IsToolActive(typeof(FoVTool)))
  131. {
  132. CinemachineSceneToolHelpers.FovToolHandle(freelook,
  133. new SerializedObject(freelook).FindProperty(() => freelook.m_Lens),
  134. freelook.m_Lens, IsHorizontalFOVUsed());
  135. }
  136. else if (freelook.m_CommonLens && CinemachineSceneToolUtility.IsToolActive(typeof(FarNearClipTool)))
  137. {
  138. CinemachineSceneToolHelpers.NearFarClipHandle(freelook,
  139. new SerializedObject(freelook).FindProperty(() => freelook.m_Lens));
  140. }
  141. else if (freelook.Follow != null && CinemachineSceneToolUtility.IsToolActive(typeof(FollowOffsetTool)))
  142. {
  143. var draggedRig = CinemachineSceneToolHelpers.OrbitControlHandle(freelook,
  144. new SerializedObject(freelook).FindProperty(() => freelook.m_Orbits));
  145. if (draggedRig >= 0)
  146. SetSelectedRig(Target, draggedRig);
  147. }
  148. Handles.color = originalColor;
  149. }
  150. #endif
  151. static GUIContent[] s_RigNames =
  152. {
  153. new GUIContent("Top Rig"),
  154. new GUIContent("Middle Rig"),
  155. new GUIContent("Bottom Rig")
  156. };
  157. internal static GUIContent[] RigNames => s_RigNames;
  158. static int GetSelectedRig(CinemachineFreeLook freelook)
  159. {
  160. return freelook.m_YAxis.Value < 0.33f ? 2 : (freelook.m_YAxis.Value > 0.66f ? 0 : 1);
  161. }
  162. internal static void SetSelectedRig(CinemachineFreeLook freelook, int rigIndex)
  163. {
  164. Debug.Assert(rigIndex >= 0 && rigIndex < 3);
  165. if (GetSelectedRig(freelook) != rigIndex)
  166. {
  167. var prop = new SerializedObject(freelook).FindProperty(
  168. () => freelook.m_YAxis).FindPropertyRelative(() => freelook.m_YAxis.Value);
  169. prop.floatValue = rigIndex == 0 ? 1 : (rigIndex == 1 ? 0.5f : 0);
  170. prop.serializedObject.ApplyModifiedProperties();
  171. freelook.InternalUpdateCameraState(Vector3.up, -1);
  172. }
  173. #if UNITY_2021_2_OR_NEWER
  174. // Push current selection to the rig selection tool
  175. //FreelookRigSelection.SelectedRig = rigIndex;
  176. #endif
  177. }
  178. UnityEditor.Editor m_rigEditor;
  179. System.Reflection.MethodInfo m_RigEditorOnSceneGUI;
  180. CinemachineVirtualCameraBase m_EditedRig = null;
  181. void UpdateRigEditor()
  182. {
  183. var selectedRig = GetSelectedRig(Target);
  184. CinemachineVirtualCamera rig = Target.GetRig(selectedRig);
  185. if (m_EditedRig != rig || m_rigEditor == null)
  186. {
  187. m_EditedRig = rig;
  188. m_RigEditorOnSceneGUI = null;
  189. if (m_rigEditor != null)
  190. {
  191. UnityEngine.Object.DestroyImmediate(m_rigEditor);
  192. m_rigEditor = null;
  193. }
  194. if (rig != null)
  195. {
  196. CreateCachedEditor(rig, null, ref m_rigEditor);
  197. if (m_rigEditor != null)
  198. m_RigEditorOnSceneGUI = m_rigEditor.GetType().GetMethod("OnSceneGUI",
  199. System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  200. }
  201. }
  202. }
  203. /// <summary>
  204. /// Register with CinemachineFreeLook to create the pipeline in an undo-friendly manner
  205. /// </summary>
  206. [InitializeOnLoad]
  207. class CreateRigWithUndo
  208. {
  209. static CreateRigWithUndo()
  210. {
  211. CinemachineFreeLook.CreateRigOverride
  212. = (CinemachineFreeLook vcam, string name, CinemachineVirtualCamera copyFrom) =>
  213. {
  214. // Recycle the game object if it exists
  215. GameObject go = null;
  216. foreach (Transform child in vcam.transform)
  217. {
  218. if (child.gameObject.name == name)
  219. {
  220. go = child.gameObject;
  221. break;
  222. }
  223. }
  224. CinemachineVirtualCamera rig = null;
  225. if (go == null)
  226. {
  227. // Create a new rig - can't do it if prefab instance
  228. if (PrefabUtility.IsPartOfAnyPrefab(vcam.gameObject))
  229. return null;
  230. go = ObjectFactory.CreateGameObject(name);
  231. Undo.RegisterCreatedObjectUndo(go, "created rig");
  232. Undo.SetTransformParent(go.transform, vcam.transform, "parenting pipeline");
  233. }
  234. // Create a new rig with default components
  235. rig = Undo.AddComponent<CinemachineVirtualCamera>(go);
  236. rig.CreatePipeline(copyFrom);
  237. if (copyFrom != null)
  238. ReflectionHelpers.CopyFields(copyFrom, rig);
  239. else
  240. {
  241. // Defaults
  242. go = rig.GetComponentOwner().gameObject;
  243. Undo.AddComponent<CinemachineOrbitalTransposer>(go);
  244. Undo.AddComponent<CinemachineComposer>(go);
  245. rig.InvalidateComponentPipeline();
  246. }
  247. return rig;
  248. };
  249. CinemachineFreeLook.DestroyRigOverride = (GameObject rig) =>
  250. {
  251. var vcam = rig.GetComponent<CinemachineVirtualCamera>();
  252. if (vcam != null)
  253. {
  254. vcam.DestroyPipeline();
  255. Undo.DestroyObjectImmediate(vcam);
  256. }
  257. };
  258. }
  259. }
  260. [DrawGizmo(GizmoType.Active | GizmoType.Selected, typeof(CinemachineFreeLook))]
  261. private static void DrawFreeLookGizmos(CinemachineFreeLook vcam, GizmoType selectionType)
  262. {
  263. // Standard frustum and logo
  264. CinemachineBrainEditor.DrawVirtualCameraBaseGizmos(vcam, selectionType);
  265. Color originalGizmoColour = Gizmos.color;
  266. bool isActiveVirtualCam = CinemachineCore.Instance.IsLive(vcam);
  267. Gizmos.color = isActiveVirtualCam
  268. ? CinemachineSettings.CinemachineCoreSettings.ActiveGizmoColour
  269. : CinemachineSettings.CinemachineCoreSettings.InactiveGizmoColour;
  270. if (vcam.Follow != null)
  271. {
  272. Vector3 pos = vcam.Follow.position;
  273. Vector3 up = vcam.State.ReferenceUp;
  274. var MiddleRig = vcam.GetRig(1).GetCinemachineComponent<CinemachineOrbitalTransposer>();
  275. if (MiddleRig != null)
  276. {
  277. Quaternion orient = MiddleRig.GetReferenceOrientation(up);
  278. up = orient * Vector3.up;
  279. float rotation = vcam.m_XAxis.Value + vcam.m_Heading.m_Bias;
  280. orient = Quaternion.AngleAxis(rotation, up) * orient;
  281. CinemachineOrbitalTransposerEditor.DrawCircleAtPointWithRadius(
  282. pos + up * vcam.m_Orbits[0].m_Height, orient, vcam.m_Orbits[0].m_Radius);
  283. CinemachineOrbitalTransposerEditor.DrawCircleAtPointWithRadius(
  284. pos + up * vcam.m_Orbits[1].m_Height, orient, vcam.m_Orbits[1].m_Radius);
  285. CinemachineOrbitalTransposerEditor.DrawCircleAtPointWithRadius(
  286. pos + up * vcam.m_Orbits[2].m_Height, orient, vcam.m_Orbits[2].m_Radius);
  287. DrawCameraPath(pos, orient, vcam);
  288. }
  289. }
  290. Gizmos.color = originalGizmoColour;
  291. }
  292. private static void DrawCameraPath(Vector3 atPos, Quaternion orient, CinemachineFreeLook vcam)
  293. {
  294. Matrix4x4 prevMatrix = Gizmos.matrix;
  295. Gizmos.matrix = Matrix4x4.TRS(atPos, orient, Vector3.one);
  296. const int kNumSteps = 20;
  297. Vector3 currPos = vcam.GetLocalPositionForCameraFromInput(0f);
  298. for (int i = 1; i < kNumSteps + 1; ++i)
  299. {
  300. float t = (float)i / (float)kNumSteps;
  301. Vector3 nextPos = vcam.GetLocalPositionForCameraFromInput(t);
  302. Gizmos.DrawLine(currPos, nextPos);
  303. currPos = nextPos;
  304. }
  305. Gizmos.matrix = prevMatrix;
  306. }
  307. }
  308. }