Cinemachine3rdPersonFollowEditor.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using UnityEngine;
  2. using UnityEditor;
  3. namespace Cinemachine.Editor
  4. {
  5. [CustomEditor(typeof(Cinemachine3rdPersonFollow))]
  6. [CanEditMultipleObjects]
  7. internal class Cinemachine3rdPersonFollowEditor : BaseEditor<Cinemachine3rdPersonFollow>
  8. {
  9. [DrawGizmo(GizmoType.Active | GizmoType.Selected, typeof(Cinemachine3rdPersonFollow))]
  10. static void Draw3rdPersonGizmos(Cinemachine3rdPersonFollow target, GizmoType selectionType)
  11. {
  12. if (target.IsValid)
  13. {
  14. var isLive = CinemachineCore.Instance.IsLive(target.VirtualCamera);
  15. Color originalGizmoColour = Gizmos.color;
  16. Gizmos.color = isLive
  17. ? CinemachineSettings.CinemachineCoreSettings.ActiveGizmoColour
  18. : CinemachineSettings.CinemachineCoreSettings.InactiveGizmoColour;
  19. target.GetRigPositions(out Vector3 root, out Vector3 shoulder, out Vector3 hand);
  20. Gizmos.DrawLine(root, shoulder);
  21. Gizmos.DrawLine(shoulder, hand);
  22. Gizmos.DrawSphere(root, 0.02f);
  23. Gizmos.DrawSphere(shoulder, 0.02f);
  24. #if CINEMACHINE_PHYSICS
  25. Gizmos.DrawSphere(hand, target.CameraRadius);
  26. if (isLive)
  27. Gizmos.color = CinemachineSettings.CinemachineCoreSettings.BoundaryObjectGizmoColour;
  28. Gizmos.DrawSphere(target.VirtualCamera.State.RawPosition, target.CameraRadius);
  29. #endif
  30. Gizmos.color = originalGizmoColour;
  31. }
  32. }
  33. public override void OnInspectorGUI()
  34. {
  35. BeginInspector();
  36. bool needWarning = false;
  37. for (int i = 0; !needWarning && i < targets.Length; ++i)
  38. needWarning = (targets[i] as Cinemachine3rdPersonFollow).FollowTarget == null;
  39. if (needWarning)
  40. EditorGUILayout.HelpBox(
  41. "3rd Person Follow requires a Follow Target. Change Body to Do Nothing if you don't want a Follow target.",
  42. MessageType.Warning);
  43. DrawRemainingPropertiesInInspector();
  44. }
  45. #if UNITY_2021_2_OR_NEWER
  46. protected virtual void OnEnable()
  47. {
  48. CinemachineSceneToolUtility.RegisterTool(typeof(FollowOffsetTool));
  49. }
  50. protected virtual void OnDisable()
  51. {
  52. CinemachineSceneToolUtility.UnregisterTool(typeof(FollowOffsetTool));
  53. }
  54. void OnSceneGUI()
  55. {
  56. DrawSceneTools();
  57. }
  58. void DrawSceneTools()
  59. {
  60. var thirdPerson = Target;
  61. if (thirdPerson == null || !thirdPerson.IsValid)
  62. {
  63. return;
  64. }
  65. if (CinemachineSceneToolUtility.IsToolActive(typeof(FollowOffsetTool)))
  66. {
  67. var originalColor = Handles.color;
  68. thirdPerson.GetRigPositions(out var followTargetPosition, out var shoulderPosition,
  69. out var armPosition);
  70. var followTargetRotation = thirdPerson.FollowTargetRotation;
  71. var targetForward = followTargetRotation * Vector3.forward;
  72. var heading = Cinemachine3rdPersonFollow.GetHeading(
  73. followTargetRotation, thirdPerson.VirtualCamera.State.ReferenceUp);
  74. EditorGUI.BeginChangeCheck();
  75. // shoulder handle
  76. #if UNITY_2022_2_OR_NEWER
  77. var sHandleIds = Handles.PositionHandleIds.@default;
  78. var newShoulderPosition = Handles.PositionHandle(sHandleIds, shoulderPosition, heading);
  79. var sHandleMinId = sHandleIds.x - 1;
  80. var sHandleMaxId = sHandleIds.xyz + 1;
  81. #else
  82. var sHandleMinId = GUIUtility.GetControlID(FocusType.Passive);
  83. var newShoulderPosition = Handles.PositionHandle(shoulderPosition, heading);
  84. var sHandleMaxId = GUIUtility.GetControlID(FocusType.Passive);
  85. #endif
  86. Handles.color = Handles.preselectionColor;
  87. // arm handle
  88. var followUp = followTargetRotation * Vector3.up;
  89. var aHandleId = GUIUtility.GetControlID(FocusType.Passive);
  90. var newArmPosition = Handles.Slider(aHandleId, armPosition, followUp,
  91. CinemachineSceneToolHelpers.CubeHandleCapSize(armPosition), Handles.CubeHandleCap, 0.5f);
  92. // cam distance handle
  93. var camDistance = thirdPerson.CameraDistance;
  94. var camPos = armPosition - targetForward * camDistance;
  95. var cdHandleId = GUIUtility.GetControlID(FocusType.Passive);
  96. var newCamPos = Handles.Slider(cdHandleId, camPos, targetForward,
  97. CinemachineSceneToolHelpers.CubeHandleCapSize(camPos), Handles.CubeHandleCap, 0.5f);
  98. if (EditorGUI.EndChangeCheck())
  99. {
  100. // Modify via SerializedProperty for OnValidate to get called automatically, and scene repainting too
  101. var so = new SerializedObject(thirdPerson);
  102. var shoulderOffset = so.FindProperty(() => thirdPerson.ShoulderOffset);
  103. shoulderOffset.vector3Value +=
  104. CinemachineSceneToolHelpers.PositionHandleDelta(heading, newShoulderPosition, shoulderPosition);
  105. var verticalArmLength = so.FindProperty(() => thirdPerson.VerticalArmLength);
  106. verticalArmLength.floatValue +=
  107. CinemachineSceneToolHelpers.SliderHandleDelta(newArmPosition, armPosition, followUp);
  108. var cameraDistance = so.FindProperty(() => thirdPerson.CameraDistance);
  109. cameraDistance.floatValue -=
  110. CinemachineSceneToolHelpers.SliderHandleDelta(newCamPos, camPos, targetForward);
  111. so.ApplyModifiedProperties();
  112. }
  113. var isDragged = IsHandleDragged(sHandleMinId, sHandleMaxId, shoulderPosition, "Shoulder Offset "
  114. + thirdPerson.ShoulderOffset.ToString("F1"), followTargetPosition, shoulderPosition);
  115. isDragged |= IsHandleDragged(aHandleId, aHandleId, armPosition, "Vertical Arm Length ("
  116. + thirdPerson.VerticalArmLength.ToString("F1") + ")", shoulderPosition, armPosition);
  117. isDragged |= IsHandleDragged(cdHandleId, cdHandleId, camPos, "Camera Distance ("
  118. + camDistance.ToString("F1") + ")", armPosition, camPos);
  119. CinemachineSceneToolHelpers.SoloOnDrag(isDragged, thirdPerson.VirtualCamera, sHandleMaxId);
  120. Handles.color = originalColor;
  121. }
  122. // local function that draws label and guide lines, and returns true if a handle has been dragged
  123. static bool IsHandleDragged
  124. (int handleMinId, int handleMaxId, Vector3 labelPos, string text, Vector3 lineStart, Vector3 lineEnd)
  125. {
  126. bool handleIsDragged;
  127. bool handleIsDraggedOrHovered;
  128. if (handleMinId == handleMaxId) {
  129. handleIsDragged = GUIUtility.hotControl == handleMinId;
  130. handleIsDraggedOrHovered = handleIsDragged || HandleUtility.nearestControl == handleMinId;
  131. }
  132. else
  133. {
  134. handleIsDragged = handleMinId < GUIUtility.hotControl && GUIUtility.hotControl < handleMaxId;
  135. handleIsDraggedOrHovered = handleIsDragged ||
  136. (handleMinId < HandleUtility.nearestControl && HandleUtility.nearestControl < handleMaxId);
  137. }
  138. if (handleIsDraggedOrHovered)
  139. CinemachineSceneToolHelpers.DrawLabel(labelPos, text);
  140. Handles.color = handleIsDraggedOrHovered ?
  141. Handles.selectedColor : CinemachineSceneToolHelpers.HelperLineDefaultColor;
  142. Handles.DrawLine(lineStart, lineEnd, CinemachineSceneToolHelpers.LineThickness);
  143. return handleIsDragged;
  144. }
  145. }
  146. #endif
  147. }
  148. }