using UnityEditor; using UnityEditor.UI; using UnityEngine; using System.Collections.Generic; using System.Linq; namespace Coffee.UIExtensions { [CustomEditor(typeof(UIParticle))] [CanEditMultipleObjects] internal class UIParticleEditor : GraphicEditor { //################################ // Constant or Static Members. //################################ private static readonly GUIContent s_ContentParticleMaterial = new GUIContent("Particle Material", "The material for rendering particles"); private static readonly GUIContent s_ContentTrailMaterial = new GUIContent("Trail Material", "The material for rendering particle trails"); private static readonly GUIContent s_ContentAdvancedOptions = new GUIContent("Advanced Options"); private static readonly GUIContent s_Content3D = new GUIContent("3D"); private static readonly GUIContent s_ContentScale = new GUIContent("Scale"); private static readonly List s_ParticleSystems = new List(); private SerializedProperty _spParticleSystem; private SerializedProperty _spScale3D; private SerializedProperty _spIgnoreCanvasScaler; private SerializedProperty _spAnimatableProperties; private bool _xyzMode; private static readonly List s_MaskablePropertyNames = new List { "_Stencil", "_StencilComp", "_StencilOp", "_StencilWriteMask", "_StencilReadMask", "_ColorMask", }; //################################ // Public/Protected Members. //################################ /// /// This function is called when the object becomes enabled and active. /// protected override void OnEnable() { base.OnEnable(); _spParticleSystem = serializedObject.FindProperty("m_ParticleSystem"); _spScale3D = serializedObject.FindProperty("m_Scale3D"); _spIgnoreCanvasScaler = serializedObject.FindProperty("m_IgnoreCanvasScaler"); _spAnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties"); } /// /// Implement this function to make a custom inspector. /// public override void OnInspectorGUI() { var current = target as UIParticle; if (current == null) return; serializedObject.Update(); EditorGUI.BeginDisabledGroup(true); EditorGUILayout.PropertyField(_spParticleSystem); EditorGUI.EndDisabledGroup(); // Draw materials. EditorGUI.indentLevel++; var ps = _spParticleSystem.objectReferenceValue as ParticleSystem; if (ps != null) { var pr = ps.GetComponent(); var sp = new SerializedObject(pr).FindProperty("m_Materials"); EditorGUI.BeginChangeCheck(); { EditorGUILayout.PropertyField(sp.GetArrayElementAtIndex(0), s_ContentParticleMaterial); if (2 <= sp.arraySize) { EditorGUILayout.PropertyField(sp.GetArrayElementAtIndex(1), s_ContentTrailMaterial); } } if (EditorGUI.EndChangeCheck()) { sp.serializedObject.ApplyModifiedProperties(); } if (!Application.isPlaying && pr.enabled) { EditorGUILayout.HelpBox("UIParticles disable the RendererModule in ParticleSystem at runtime to prevent double rendering.", MessageType.Warning); } } EditorGUI.indentLevel--; // Advanced Options EditorGUILayout.Space(); EditorGUILayout.LabelField(s_ContentAdvancedOptions, EditorStyles.boldLabel); _xyzMode = DrawFloatOrVector3Field(_spScale3D, _xyzMode); EditorGUILayout.PropertyField(_spIgnoreCanvasScaler); // AnimatableProperties AnimatedPropertiesEditor.DrawAnimatableProperties(_spAnimatableProperties, current.material); // Fix current.GetComponentsInChildren(true, s_ParticleSystems); if (s_ParticleSystems.Any(x => x.GetComponent() == null)) { GUILayout.BeginHorizontal(); EditorGUILayout.HelpBox("There are child ParticleSystems that does not have a UIParticle component.\nAdd UIParticle component to them.", MessageType.Warning); GUILayout.BeginVertical(); if (GUILayout.Button("Fix")) { foreach (var p in s_ParticleSystems.Where(x => !x.GetComponent())) { p.gameObject.AddComponent(); } } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } s_ParticleSystems.Clear(); // Does the shader support UI masks? if (current.maskable && current.material && current.material.shader) { var mat = current.material; var shader = mat.shader; foreach (var propName in s_MaskablePropertyNames) { if (mat.HasProperty(propName)) continue; EditorGUILayout.HelpBox(string.Format("Shader '{0}' doesn't have '{1}' property. This graphic cannot be masked.", shader.name, propName), MessageType.Warning); break; } } serializedObject.ApplyModifiedProperties(); } private static bool DrawFloatOrVector3Field(SerializedProperty sp, bool showXyz) { var x = sp.FindPropertyRelative("x"); var y = sp.FindPropertyRelative("y"); var z = sp.FindPropertyRelative("z"); showXyz |= !Mathf.Approximately(x.floatValue, y.floatValue) || !Mathf.Approximately(y.floatValue, z.floatValue) || y.hasMultipleDifferentValues || z.hasMultipleDifferentValues; EditorGUILayout.BeginHorizontal(); if (showXyz) { EditorGUILayout.PropertyField(sp); } else { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(x, s_ContentScale); if (EditorGUI.EndChangeCheck()) z.floatValue = y.floatValue = x.floatValue; } EditorGUI.BeginChangeCheck(); showXyz = GUILayout.Toggle(showXyz, s_Content3D, EditorStyles.miniButton, GUILayout.Width(30)); if (EditorGUI.EndChangeCheck() && !showXyz) z.floatValue = y.floatValue = x.floatValue; EditorGUILayout.EndHorizontal(); return showXyz; } } }