UIParticleEditor.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. using UnityEditor;
  2. using UnityEditor.UI;
  3. using UnityEngine;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using UnityEditorInternal;
  7. using UnityEngine.UI;
  8. using UnityEditor.Experimental.SceneManagement;
  9. using UnityEditor.SceneManagement;
  10. namespace Coffee.UIExtensions
  11. {
  12. [CustomEditor(typeof(UIParticle))]
  13. [CanEditMultipleObjects]
  14. internal class UIParticleEditor : GraphicEditor
  15. {
  16. //################################
  17. // Constant or Static Members.
  18. //################################
  19. private static readonly GUIContent s_ContentRenderingOrder = new GUIContent("Rendering Order");
  20. private static readonly GUIContent s_ContentRefresh = new GUIContent("Refresh");
  21. private static readonly GUIContent s_ContentFix = new GUIContent("Fix");
  22. private static readonly GUIContent s_ContentMaterial = new GUIContent("Material");
  23. private static readonly GUIContent s_ContentTrailMaterial = new GUIContent("Trail Material");
  24. private static readonly GUIContent s_Content3D = new GUIContent("3D");
  25. private static readonly GUIContent s_ContentScale = new GUIContent("Scale");
  26. private SerializedProperty _spMaskable;
  27. private SerializedProperty _spScale;
  28. private SerializedProperty _spIgnoreCanvasScaler;
  29. private SerializedProperty _spAnimatableProperties;
  30. private SerializedProperty _spShrinkByMaterial;
  31. private ReorderableList _ro;
  32. private bool _xyzMode;
  33. private bool _showMaterials;
  34. private static readonly List<string> s_MaskablePropertyNames = new List<string>
  35. {
  36. "_Stencil",
  37. "_StencilComp",
  38. "_StencilOp",
  39. "_StencilWriteMask",
  40. "_StencilReadMask",
  41. "_ColorMask",
  42. };
  43. //################################
  44. // Public/Protected Members.
  45. //################################
  46. /// <summary>
  47. /// This function is called when the object becomes enabled and active.
  48. /// </summary>
  49. protected override void OnEnable()
  50. {
  51. base.OnEnable();
  52. _spMaskable = serializedObject.FindProperty("m_Maskable");
  53. _spScale = serializedObject.FindProperty("m_Scale3D");
  54. _spIgnoreCanvasScaler = serializedObject.FindProperty("m_IgnoreCanvasScaler");
  55. _spAnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
  56. _spShrinkByMaterial = serializedObject.FindProperty("m_ShrinkByMaterial");
  57. _showMaterials = EditorPrefs.GetBool("Coffee.UIExtensions.UIParticleEditor._showMaterials", true);
  58. var sp = serializedObject.FindProperty("m_Particles");
  59. _ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true);
  60. _ro.elementHeight = EditorGUIUtility.singleLineHeight * 3 + 4;
  61. _ro.elementHeightCallback = _ => _showMaterials
  62. ? 3 * (EditorGUIUtility.singleLineHeight + 2)
  63. : EditorGUIUtility.singleLineHeight + 2;
  64. _ro.drawElementCallback = (rect, index, active, focused) =>
  65. {
  66. EditorGUI.BeginDisabledGroup(sp.hasMultipleDifferentValues);
  67. rect.y += 1;
  68. rect.height = EditorGUIUtility.singleLineHeight;
  69. var p = sp.GetArrayElementAtIndex(index);
  70. EditorGUI.ObjectField(rect, p, GUIContent.none);
  71. if (!_showMaterials) return;
  72. rect.x += 15;
  73. rect.width -= 15;
  74. var ps = p.objectReferenceValue as ParticleSystem;
  75. var materials = ps
  76. ? new SerializedObject(ps.GetComponent<ParticleSystemRenderer>()).FindProperty("m_Materials")
  77. : null;
  78. rect.y += rect.height + 1;
  79. MaterialField(rect, s_ContentMaterial, materials, 0);
  80. rect.y += rect.height + 1;
  81. MaterialField(rect, s_ContentTrailMaterial, materials, 1);
  82. EditorGUI.EndDisabledGroup();
  83. if (materials != null)
  84. {
  85. materials.serializedObject.ApplyModifiedProperties();
  86. }
  87. };
  88. _ro.drawHeaderCallback += rect =>
  89. {
  90. #if !UNITY_2019_3_OR_NEWER
  91. rect.y -= 1;
  92. #endif
  93. EditorGUI.LabelField(new Rect(rect.x, rect.y, 150, rect.height), s_ContentRenderingOrder);
  94. var content = EditorGUIUtility.IconContent(_showMaterials ? "VisibilityOn" : "VisibilityOff");
  95. _showMaterials = GUI.Toggle(new Rect(rect.width - 55, rect.y, 24, 20), _showMaterials, content, EditorStyles.label);
  96. if (GUI.Button(new Rect(rect.width - 35, rect.y, 60, rect.height), s_ContentRefresh, EditorStyles.miniButton))
  97. {
  98. foreach (UIParticle t in targets)
  99. {
  100. t.RefreshParticles();
  101. }
  102. }
  103. };
  104. }
  105. private static void MaterialField(Rect rect, GUIContent label, SerializedProperty sp, int index)
  106. {
  107. if (sp == null || sp.arraySize <= index)
  108. {
  109. EditorGUI.BeginDisabledGroup(true);
  110. EditorGUI.ObjectField(rect, label, null, typeof(Material), true);
  111. EditorGUI.EndDisabledGroup();
  112. }
  113. else
  114. {
  115. EditorGUI.PropertyField(rect, sp.GetArrayElementAtIndex(index), label);
  116. }
  117. }
  118. /// <summary>
  119. /// Implement this function to make a custom inspector.
  120. /// </summary>
  121. public override void OnInspectorGUI()
  122. {
  123. var current = target as UIParticle;
  124. if (current == null) return;
  125. serializedObject.Update();
  126. // Maskable
  127. EditorGUILayout.PropertyField(_spMaskable);
  128. // IgnoreCanvasScaler
  129. using (var ccs = new EditorGUI.ChangeCheckScope())
  130. {
  131. EditorGUILayout.PropertyField(_spIgnoreCanvasScaler);
  132. if (ccs.changed)
  133. {
  134. foreach (UIParticle p in targets)
  135. {
  136. p.ignoreCanvasScaler = _spIgnoreCanvasScaler.boolValue;
  137. }
  138. }
  139. }
  140. // Scale
  141. _xyzMode = DrawFloatOrVector3Field(_spScale, _xyzMode);
  142. // AnimatableProperties
  143. var mats = current.particles
  144. .Where(x => x)
  145. .Select(x => x.GetComponent<ParticleSystemRenderer>().sharedMaterial)
  146. .Where(x => x)
  147. .ToArray();
  148. // Animated properties
  149. EditorGUI.BeginChangeCheck();
  150. AnimatedPropertiesEditor.DrawAnimatableProperties(_spAnimatableProperties, mats);
  151. if (EditorGUI.EndChangeCheck())
  152. {
  153. foreach (UIParticle t in targets)
  154. t.SetMaterialDirty();
  155. }
  156. // ShrinkByMaterial
  157. EditorGUILayout.PropertyField(_spShrinkByMaterial);
  158. // Target ParticleSystems.
  159. _ro.DoLayoutList();
  160. serializedObject.ApplyModifiedProperties();
  161. // Does the shader support UI masks?
  162. if (current.maskable && current.GetComponentInParent<Mask>())
  163. {
  164. foreach (var mat in current.materials)
  165. {
  166. if (!mat || !mat.shader) continue;
  167. var shader = mat.shader;
  168. foreach (var propName in s_MaskablePropertyNames)
  169. {
  170. if (mat.HasProperty(propName)) continue;
  171. EditorGUILayout.HelpBox(string.Format("Shader '{0}' doesn't have '{1}' property. This graphic cannot be masked.", shader.name, propName), MessageType.Warning);
  172. break;
  173. }
  174. }
  175. }
  176. // Does the shader support UI masks?
  177. if (FixButton(current.m_IsTrail, "This UIParticle component should be removed. The UIParticle for trails is no longer needed."))
  178. {
  179. DestroyUIParticle(current);
  180. return;
  181. }
  182. }
  183. void DestroyUIParticle(UIParticle p, bool ignoreCurrent = false)
  184. {
  185. if (!p || ignoreCurrent && target == p) return;
  186. var cr = p.canvasRenderer;
  187. DestroyImmediate(p);
  188. DestroyImmediate(cr);
  189. #if UNITY_2018_3_OR_NEWER
  190. var stage = PrefabStageUtility.GetCurrentPrefabStage();
  191. if (stage != null && stage.scene.isLoaded)
  192. {
  193. #if UNITY_2020_1_OR_NEWER
  194. string prefabAssetPath = stage.assetPath;
  195. #else
  196. string prefabAssetPath = stage.prefabAssetPath;
  197. #endif
  198. PrefabUtility.SaveAsPrefabAsset(stage.prefabContentsRoot, prefabAssetPath);
  199. }
  200. #endif
  201. }
  202. bool FixButton(bool show, string text)
  203. {
  204. if (!show) return false;
  205. using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(true)))
  206. {
  207. EditorGUILayout.HelpBox(text, MessageType.Warning, true);
  208. using (new EditorGUILayout.VerticalScope())
  209. {
  210. return GUILayout.Button(s_ContentFix, GUILayout.Width(30));
  211. }
  212. }
  213. }
  214. private static bool DrawFloatOrVector3Field(SerializedProperty sp, bool showXyz)
  215. {
  216. var x = sp.FindPropertyRelative("x");
  217. var y = sp.FindPropertyRelative("y");
  218. var z = sp.FindPropertyRelative("z");
  219. showXyz |= !Mathf.Approximately(x.floatValue, y.floatValue) ||
  220. !Mathf.Approximately(y.floatValue, z.floatValue) ||
  221. y.hasMultipleDifferentValues ||
  222. z.hasMultipleDifferentValues;
  223. EditorGUILayout.BeginHorizontal();
  224. if (showXyz)
  225. {
  226. EditorGUI.BeginChangeCheck();
  227. EditorGUILayout.PropertyField(sp);
  228. if (EditorGUI.EndChangeCheck())
  229. {
  230. x.floatValue = Mathf.Max(0.001f, x.floatValue);
  231. y.floatValue = Mathf.Max(0.001f, y.floatValue);
  232. z.floatValue = Mathf.Max(0.001f, z.floatValue);
  233. }
  234. }
  235. else
  236. {
  237. EditorGUI.BeginChangeCheck();
  238. EditorGUILayout.PropertyField(x, s_ContentScale);
  239. if (EditorGUI.EndChangeCheck())
  240. {
  241. x.floatValue = Mathf.Max(0.001f, x.floatValue);
  242. y.floatValue = Mathf.Max(0.001f, x.floatValue);
  243. z.floatValue = Mathf.Max(0.001f, x.floatValue);
  244. }
  245. }
  246. EditorGUI.BeginChangeCheck();
  247. showXyz = GUILayout.Toggle(showXyz, s_Content3D, EditorStyles.miniButton, GUILayout.Width(30));
  248. if (EditorGUI.EndChangeCheck() && !showXyz)
  249. z.floatValue = y.floatValue = x.floatValue;
  250. EditorGUILayout.EndHorizontal();
  251. return showXyz;
  252. }
  253. }
  254. }