ParticleEffectForUGUI/Scripts/Editor/UIParticleEditor.cs

281 lines
12 KiB
C#
Raw Normal View History

2018-06-22 18:48:14 +08:00
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;
2018-11-28 18:55:56 +08:00
using System.Collections.Generic;
using System.Linq;
using System;
2019-02-23 22:20:30 +08:00
using ShaderPropertyType = Coffee.UIExtensions.UIParticle.AnimatableProperty.ShaderPropertyType;
2018-06-22 18:48:14 +08:00
namespace Coffee.UIExtensions
{
3.0.0-preview.1 # [3.0.0-preview.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v2.3.0...v3.0.0-preview.1) (2020-02-12) ### Bug Fixes * change the text in the inspector to make it more understandable. ([7ca0b6f](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/7ca0b6fa34c1168ef103992e1c69b68631b3bc60)), closes [#66](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/66) * editor crashes when mesh is set to null when ParticleSystem.RenderMode=Mesh ([e5ebadd](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e5ebadd84731716f82c63ac5ba3eb676720e3ad6)), closes [#69](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/69) * getting massive errors on editor ([ef82fa8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/ef82fa82a69894e643f0d257f99eb9177785f697)), closes [#67](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/67) * heavy editor UI ([d3b470a](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/d3b470a2be3a21add9e126b357e46bdfaa6f16c8)) * remove a menu to add overlay camera ([f5d3b6e](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/f5d3b6edb5687c4d465992ef5c3c0d54f7b36d74)) * rotating the particle rect may cause out of bounds ([3439842](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/3439842119f334b50910c0a983e561944cc792a2)), closes [#55](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/55) * scale will be decrease on editor ([0c59846](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/0c59846f11438b12caad04ae5d5b544a28883ea6)) * UI darker than normal when change to linear color space ([db4d8a7](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/db4d8a742ca36c8dd2de6936b9cf2912c72d4b9f)), closes [#57](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/57) ### Build System * update develop environment ([9fcf169](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/9fcf169cd3ced519611b2ede7f98ad4d678027c6)) ### Features * add menu to import sample ([b8b1827](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/b8b18273185769235101da01f5bbadbac188e387)) * add samples test ([287b5cc](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/287b5cc832f2899796227520bda4d11ad8e4fae9)) * **editor:** add osc package (portable mode) ([6c7f880](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/6c7f8804350112505949c5c296f9e0340877a3e8)) ### BREAKING CHANGES * update develop environment to Unity 2018.3. Unity 2018.2 will continue to be supported.
2020-02-12 20:38:06 +08:00
[CustomEditor(typeof(UIParticle))]
[CanEditMultipleObjects]
public class UIParticleEditor : GraphicEditor
{
class AnimatedPropertiesEditor
{
static readonly List<string> s_ActiveNames = new List<string>();
static readonly System.Text.StringBuilder s_Sb = new System.Text.StringBuilder();
public string name;
public ShaderPropertyType type;
static string CollectActiveNames(SerializedProperty sp, List<string> result)
{
result.Clear();
for (int i = 0; i < sp.arraySize; i++)
{
result.Add(sp.GetArrayElementAtIndex(i).FindPropertyRelative("m_Name").stringValue);
}
s_Sb.Length = 0;
if (result.Count == 0)
{
s_Sb.Append("Nothing");
}
else
{
result.Aggregate(s_Sb, (a, b) => s_Sb.AppendFormat("{0}, ", b));
s_Sb.Length -= 2;
}
return s_Sb.ToString();
}
public static void DrawAnimatableProperties(SerializedProperty sp, Material mat)
{
if (!mat || !mat.shader)
return;
bool isClicked = false;
using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(false)))
{
var r = EditorGUI.PrefixLabel(EditorGUILayout.GetControlRect(true), new GUIContent(sp.displayName, sp.tooltip));
isClicked = GUI.Button(r, CollectActiveNames(sp, s_ActiveNames), EditorStyles.popup);
}
if (isClicked)
{
GenericMenu gm = new GenericMenu();
gm.AddItem(new GUIContent("Nothing"), s_ActiveNames.Count == 0, () =>
{
sp.ClearArray();
sp.serializedObject.ApplyModifiedProperties();
});
for (int i = 0; i < sp.arraySize; i++)
{
var p = sp.GetArrayElementAtIndex(i);
var name = p.FindPropertyRelative("m_Name").stringValue;
var type = (ShaderPropertyType)p.FindPropertyRelative("m_Type").intValue;
AddMenu(gm, sp, new AnimatedPropertiesEditor() { name = name, type = type }, false);
}
for (int i = 0; i < ShaderUtil.GetPropertyCount(mat.shader); i++)
{
var pName = ShaderUtil.GetPropertyName(mat.shader, i);
var type = (ShaderPropertyType)ShaderUtil.GetPropertyType(mat.shader, i);
AddMenu(gm, sp, new AnimatedPropertiesEditor() { name = pName, type = type }, true);
if (type == ShaderPropertyType.Texture)
{
AddMenu(gm, sp, new AnimatedPropertiesEditor() { name = pName + "_ST", type = ShaderPropertyType.Vector }, true);
AddMenu(gm, sp, new AnimatedPropertiesEditor() { name = pName + "_HDR", type = ShaderPropertyType.Vector }, true);
AddMenu(gm, sp, new AnimatedPropertiesEditor() { name = pName + "_TexelSize", type = ShaderPropertyType.Vector }, true);
}
}
gm.ShowAsContext();
}
}
public static void AddMenu(GenericMenu menu, SerializedProperty sp, AnimatedPropertiesEditor property, bool add)
{
if (add && s_ActiveNames.Contains(property.name))
return;
menu.AddItem(new GUIContent(string.Format("{0} ({1})", property.name, property.type)), s_ActiveNames.Contains(property.name), () =>
{
var index = s_ActiveNames.IndexOf(property.name);
if (0 <= index)
{
sp.DeleteArrayElementAtIndex(index);
}
else
{
sp.InsertArrayElementAtIndex(sp.arraySize);
var p = sp.GetArrayElementAtIndex(sp.arraySize - 1);
p.FindPropertyRelative("m_Name").stringValue = property.name;
p.FindPropertyRelative("m_Type").intValue = (int)property.type;
}
sp.serializedObject.ApplyModifiedProperties();
});
}
}
//################################
// Constant or Static Members.
//################################
static readonly GUIContent s_ContentParticleMaterial = new GUIContent("Particle Material", "The material for rendering particles");
static readonly GUIContent s_ContentTrailMaterial = new GUIContent("Trail Material", "The material for rendering particle trails");
static readonly List<ParticleSystem> s_ParticleSystems = new List<ParticleSystem>();
static readonly Color s_GizmoColor = new Color(1f, 0.7f, 0.7f, 0.9f);
static readonly List<string> s_MaskablePropertyNames = new List<string>()
{
"_Stencil",
"_StencilComp",
"_StencilOp",
"_StencilWriteMask",
"_StencilReadMask",
"_ColorMask",
};
//################################
// Public/Protected Members.
//################################
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
protected override void OnEnable()
{
base.OnEnable();
_spParticleSystem = serializedObject.FindProperty("m_ParticleSystem");
_spTrailParticle = serializedObject.FindProperty("m_TrailParticle");
_spScale = serializedObject.FindProperty("m_Scale");
_spIgnoreParent = serializedObject.FindProperty("m_IgnoreParent");
_spAnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
_particles = targets.Cast<UIParticle>().ToArray();
_shapeModuleUIs = null;
var targetsGos = targets.Cast<UIParticle>().Select(x => x.gameObject).ToArray();
_inspector = Resources.FindObjectsOfTypeAll<ParticleSystemInspector>()
.FirstOrDefault(x => x.targets.Cast<ParticleSystem>().Select(x => x.gameObject).SequenceEqual(targetsGos));
}
/// <summary>
/// Implement this function to make a custom inspector.
/// </summary>
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(_spParticleSystem);
EditorGUI.indentLevel++;
var ps = _spParticleSystem.objectReferenceValue as ParticleSystem;
if (ps)
{
var pr = ps.GetComponent<ParticleSystemRenderer>();
var sp = new SerializedObject(pr).FindProperty("m_Materials");
EditorGUILayout.PropertyField(sp.GetArrayElementAtIndex(0), s_ContentParticleMaterial);
EditorGUILayout.PropertyField(sp.GetArrayElementAtIndex(1), s_ContentTrailMaterial);
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--;
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.PropertyField(_spTrailParticle);
EditorGUI.EndDisabledGroup();
var current = target as UIParticle;
EditorGUILayout.PropertyField(_spIgnoreParent);
EditorGUI.BeginDisabledGroup(!current.isRoot);
EditorGUILayout.PropertyField(_spScale);
EditorGUI.EndDisabledGroup();
// AnimatableProperties
AnimatedPropertiesEditor.DrawAnimatableProperties(_spAnimatableProperties, current.material);
current.GetComponentsInChildren<ParticleSystem>(true, s_ParticleSystems);
if (s_ParticleSystems.Any(x => x.GetComponent<UIParticle>() == 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<UIParticle>()))
{
p.gameObject.AddComponent<UIParticle>();
}
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
s_ParticleSystems.Clear();
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))
{
EditorGUILayout.HelpBox(string.Format("Shader {0} doesn't have '{1}' property. This graphic is not maskable.", shader.name, propName), MessageType.Warning);
break;
}
}
}
serializedObject.ApplyModifiedProperties();
}
//################################
// Private Members.
//################################
SerializedProperty _spParticleSystem;
SerializedProperty _spTrailParticle;
SerializedProperty _spScale;
SerializedProperty _spIgnoreParent;
SerializedProperty _spAnimatableProperties;
UIParticle[] _particles;
ShapeModuleUI[] _shapeModuleUIs;
ParticleSystemInspector _inspector;
void OnSceneGUI()
{
_shapeModuleUIs ??= _inspector?.m_ParticleEffectUI?.m_Emitters?.SelectMany(x => x.m_Modules).OfType<ShapeModuleUI>()?.ToArray();
if (_shapeModuleUIs == null || _shapeModuleUIs.Length == 0 || _shapeModuleUIs[0].GetParticleSystem() != (target as UIParticle).cachedParticleSystem)
return;
Action postAction = () => { };
Color origin = ShapeModuleUI.s_GizmoColor.m_Color;
Color originDark = ShapeModuleUI.s_GizmoColor.m_Color;
ShapeModuleUI.s_GizmoColor.m_Color = s_GizmoColor;
ShapeModuleUI.s_GizmoColor.m_OptionalDarkColor = s_GizmoColor;
_particles
.Distinct()
.Select(x => new { canvas = x.canvas, ps = x.cachedParticleSystem, scale = x.scale })
.Where(x => x.ps && x.canvas)
.ToList()
.ForEach(x =>
{
var trans = x.ps.transform;
var hasChanged = trans.hasChanged;
var localScale = trans.localScale;
postAction += () => trans.localScale = localScale;
trans.localScale = Vector3.Scale(localScale, x.canvas.rootCanvas.transform.localScale * x.scale);
});
foreach (var ui in _shapeModuleUIs)
ui.OnSceneViewGUI();
postAction();
ShapeModuleUI.s_GizmoColor.m_Color = origin;
ShapeModuleUI.s_GizmoColor.m_OptionalDarkColor = originDark;
}
}
}