No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PixelPerfectCameraEditor.cs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. using System;
  2. using UnityEngine;
  3. using UnityEngine.Rendering.Universal;
  4. namespace UnityEditor.Rendering.Universal
  5. {
  6. [CustomEditor(typeof(PixelPerfectCamera))]
  7. class PixelPerfectCameraEditor : Editor
  8. {
  9. private class Style
  10. {
  11. public GUIContent x = new GUIContent("X");
  12. public GUIContent y = new GUIContent("Y");
  13. public GUIContent assetsPPU = new GUIContent("Assets Pixels Per Unit", "The amount of pixels that make up one unit of the Scene. Set this value to match the PPU value of Sprites in the Scene.");
  14. public GUIContent refRes = new GUIContent("Reference Resolution", "The original resolution the Assets are designed for.");
  15. public GUIContent gridSnapping = new GUIContent("Grid Snapping", "Sets the snapping behavior for the camera and sprites.");
  16. public GUIContent cropFrame = new GUIContent("Crop Frame", "Crops the viewport to match the Reference Resolution, along the checked axis. Black bars will be added to fit the screen aspect ratio.");
  17. public GUIContent filterMode = new GUIContent("Filter Mode", "Use selected Filter Mode when using Stretch Fill to upscale from Reference Resolution.");
  18. public GUIContent stretchFill = new GUIContent("Stretch Fill", "If enabled, expands the viewport to fit the screen resolution while maintaining the viewport aspect ratio.");
  19. public GUIContent currentPixelRatio = new GUIContent("Current Pixel Ratio", "Ratio of the rendered Sprites compared to their original size.");
  20. public GUIContent runInEditMode = new GUIContent("Run In Edit Mode", "Enable this to preview Camera setting changes in Edit Mode. This will cause constant changes to the Scene while active.");
  21. public const string cameraStackingWarning = "Pixel Perfect Camera won't function properly if stacked with another camera.";
  22. public const string nonRenderer2DWarning = "URP Pixel Perfect Camera requires a camera using a 2D Renderer. Some features, such as Upscale Render Texture, are not supported with other Renderers.";
  23. public const string nonRenderer2DError = "URP Pixel Perfect Camera requires a camera using a 2D Renderer.";
  24. public GUIStyle centeredLabel;
  25. public Style()
  26. {
  27. centeredLabel = new GUIStyle(EditorStyles.label);
  28. centeredLabel.alignment = TextAnchor.MiddleCenter;
  29. }
  30. }
  31. private static Style m_Style;
  32. private const float k_SingleLetterLabelWidth = 15.0f;
  33. private const float k_DottedLineSpacing = 2.5f;
  34. private SerializedProperty m_AssetsPPU;
  35. private SerializedProperty m_RefResX;
  36. private SerializedProperty m_RefResY;
  37. private SerializedProperty m_CropFrame;
  38. private SerializedProperty m_FilterMode;
  39. private SerializedProperty m_GridSnapping;
  40. private Vector2 m_GameViewSize = Vector2.zero;
  41. private GUIContent m_CurrentPixelRatioValue;
  42. bool m_CameraStacking;
  43. private void LazyInit()
  44. {
  45. if (m_Style == null)
  46. m_Style = new Style();
  47. if (m_CurrentPixelRatioValue == null)
  48. m_CurrentPixelRatioValue = new GUIContent();
  49. }
  50. UniversalAdditionalCameraData GetCameraData()
  51. {
  52. PixelPerfectCamera obj = target as PixelPerfectCamera;
  53. UniversalAdditionalCameraData cameraData = null;
  54. obj?.TryGetComponent(out cameraData);
  55. return cameraData;
  56. }
  57. bool UsingSRP()
  58. {
  59. var cameraData = GetCameraData();
  60. return cameraData?.scriptableRenderer != null;
  61. }
  62. bool UsingRenderer2D()
  63. {
  64. var cameraData = GetCameraData();
  65. if (cameraData != null)
  66. {
  67. Renderer2D renderer2D = cameraData.scriptableRenderer as Renderer2D;
  68. if (renderer2D != null)
  69. return true;
  70. }
  71. return false;
  72. }
  73. void CheckForCameraStacking()
  74. {
  75. m_CameraStacking = false;
  76. var cameraData = GetCameraData();
  77. if (cameraData == null || cameraData.scriptableRenderer == null)
  78. return;
  79. if (cameraData.renderType == CameraRenderType.Base)
  80. {
  81. var cameraStack = cameraData.cameraStack;
  82. m_CameraStacking = cameraStack != null ? cameraStack.Count > 0 : false;
  83. }
  84. else if (cameraData.renderType == CameraRenderType.Overlay)
  85. m_CameraStacking = true;
  86. }
  87. public void OnEnable()
  88. {
  89. m_AssetsPPU = serializedObject.FindProperty("m_AssetsPPU");
  90. m_RefResX = serializedObject.FindProperty("m_RefResolutionX");
  91. m_RefResY = serializedObject.FindProperty("m_RefResolutionY");
  92. m_CropFrame = serializedObject.FindProperty("m_CropFrame");
  93. m_FilterMode = serializedObject.FindProperty("m_FilterMode");
  94. m_GridSnapping = serializedObject.FindProperty("m_GridSnapping");
  95. }
  96. public override bool RequiresConstantRepaint()
  97. {
  98. PixelPerfectCamera obj = target as PixelPerfectCamera;
  99. if (obj == null || !obj.enabled)
  100. return false;
  101. // If game view size changes, we need to force a repaint of the inspector as the pixel ratio value may change accordingly.
  102. Vector2 gameViewSize = Handles.GetMainGameViewSize();
  103. if (gameViewSize != m_GameViewSize)
  104. {
  105. m_GameViewSize = gameViewSize;
  106. return true;
  107. }
  108. else
  109. return false;
  110. }
  111. public override void OnInspectorGUI()
  112. {
  113. LazyInit();
  114. if (!UsingSRP())
  115. {
  116. EditorGUILayout.HelpBox(Style.nonRenderer2DError, MessageType.Error);
  117. return;
  118. }
  119. else if (!UsingRenderer2D())
  120. {
  121. EditorGUILayout.HelpBox(Style.nonRenderer2DWarning, MessageType.Warning);
  122. EditorGUILayout.Space();
  123. }
  124. float originalLabelWidth = EditorGUIUtility.labelWidth;
  125. serializedObject.Update();
  126. if (Event.current.type == EventType.Layout)
  127. CheckForCameraStacking();
  128. if (m_CameraStacking)
  129. EditorGUILayout.HelpBox(Style.cameraStackingWarning, MessageType.Warning);
  130. EditorGUILayout.PropertyField(m_AssetsPPU, m_Style.assetsPPU);
  131. if (m_AssetsPPU.intValue <= 0)
  132. m_AssetsPPU.intValue = 1;
  133. EditorGUILayout.BeginHorizontal();
  134. {
  135. EditorGUILayout.PrefixLabel(m_Style.refRes);
  136. EditorGUIUtility.labelWidth = k_SingleLetterLabelWidth * (EditorGUI.indentLevel + 1);
  137. EditorGUILayout.PropertyField(m_RefResX, m_Style.x);
  138. if (m_RefResX.intValue <= 0)
  139. m_RefResX.intValue = 1;
  140. EditorGUILayout.PropertyField(m_RefResY, m_Style.y);
  141. if (m_RefResY.intValue <= 0)
  142. m_RefResY.intValue = 1;
  143. EditorGUIUtility.labelWidth = originalLabelWidth;
  144. }
  145. EditorGUILayout.EndHorizontal();
  146. EditorGUILayout.PropertyField(m_CropFrame, m_Style.cropFrame);
  147. EditorGUILayout.PropertyField(m_GridSnapping, m_Style.gridSnapping);
  148. if (m_CropFrame.enumValueIndex == (int)PixelPerfectCamera.CropFrame.StretchFill)
  149. {
  150. EditorGUILayout.PropertyField(m_FilterMode, m_Style.filterMode);
  151. }
  152. serializedObject.ApplyModifiedProperties();
  153. PixelPerfectCamera obj = target as PixelPerfectCamera;
  154. if (obj != null)
  155. {
  156. if (obj.isActiveAndEnabled && (EditorApplication.isPlaying || obj.runInEditMode))
  157. {
  158. if (Event.current.type == EventType.Layout)
  159. m_CurrentPixelRatioValue.text = string.Format("{0}:1", obj.pixelRatio);
  160. EditorGUI.BeginDisabledGroup(true);
  161. EditorGUILayout.LabelField(m_Style.currentPixelRatio, m_CurrentPixelRatioValue);
  162. EditorGUI.EndDisabledGroup();
  163. }
  164. }
  165. }
  166. void OnSceneGUI()
  167. {
  168. PixelPerfectCamera obj = target as PixelPerfectCamera;
  169. if (obj == null)
  170. return;
  171. Camera camera = obj.GetComponent<Camera>();
  172. // Show a green rect in scene view that represents the visible area when the pixel perfect correction takes effect in play mode.
  173. Vector2 gameViewSize = Handles.GetMainGameViewSize();
  174. int gameViewWidth = (int)gameViewSize.x;
  175. int gameViewHeight = (int)gameViewSize.y;
  176. int zoom = Math.Max(1, Math.Min(gameViewHeight / obj.refResolutionY, gameViewWidth / obj.refResolutionX));
  177. float verticalOrthoSize;
  178. float horizontalOrthoSize;
  179. if (obj.cropFrame == PixelPerfectCamera.CropFrame.StretchFill || obj.cropFrame == PixelPerfectCamera.CropFrame.Windowbox)
  180. {
  181. verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
  182. horizontalOrthoSize = verticalOrthoSize * ((float)obj.refResolutionX / obj.refResolutionY);
  183. }
  184. else if (obj.cropFrame == PixelPerfectCamera.CropFrame.Letterbox)
  185. {
  186. verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
  187. horizontalOrthoSize = verticalOrthoSize * ((float)gameViewWidth / (zoom * obj.refResolutionY));
  188. }
  189. else if (obj.cropFrame == PixelPerfectCamera.CropFrame.Pillarbox)
  190. {
  191. horizontalOrthoSize = obj.refResolutionX * 0.5f / obj.assetsPPU;
  192. verticalOrthoSize = horizontalOrthoSize / (zoom * obj.refResolutionX / (float)gameViewHeight);
  193. }
  194. else
  195. {
  196. verticalOrthoSize = gameViewHeight * 0.5f / (zoom * obj.assetsPPU);
  197. horizontalOrthoSize = verticalOrthoSize * camera.aspect;
  198. }
  199. Handles.color = Color.green;
  200. Vector3 cameraPosition = camera.transform.position;
  201. Vector3 p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
  202. Vector3 p2 = cameraPosition + new Vector3(horizontalOrthoSize, verticalOrthoSize, 0.0f);
  203. Handles.DrawLine(p1, p2);
  204. p1 = cameraPosition + new Vector3(horizontalOrthoSize, -verticalOrthoSize, 0.0f);
  205. Handles.DrawLine(p2, p1);
  206. p2 = cameraPosition + new Vector3(-horizontalOrthoSize, -verticalOrthoSize, 0.0f);
  207. Handles.DrawLine(p1, p2);
  208. p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
  209. Handles.DrawLine(p2, p1);
  210. // Show a green dotted rect in scene view that represents the area defined by the reference resolution.
  211. horizontalOrthoSize = obj.refResolutionX * 0.5f / obj.assetsPPU;
  212. verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
  213. p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
  214. p2 = cameraPosition + new Vector3(horizontalOrthoSize, verticalOrthoSize, 0.0f);
  215. Handles.DrawDottedLine(p1, p2, k_DottedLineSpacing);
  216. p1 = cameraPosition + new Vector3(horizontalOrthoSize, -verticalOrthoSize, 0.0f);
  217. Handles.DrawDottedLine(p2, p1, k_DottedLineSpacing);
  218. p2 = cameraPosition + new Vector3(-horizontalOrthoSize, -verticalOrthoSize, 0.0f);
  219. Handles.DrawDottedLine(p1, p2, k_DottedLineSpacing);
  220. p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
  221. Handles.DrawDottedLine(p2, p1, k_DottedLineSpacing);
  222. }
  223. }
  224. }