using UnityEngine; using UnityEditor; using System.Collections; using System.Reflection; // Cartoon FX Easy Editor // (c) 2013-2015 - Jean Moreno public class CFXEasyEditor : EditorWindow { static private CFXEasyEditor SingleWindow; [MenuItem("Window/CartoonFX Easy Editor")] static void ShowWindow() { CFXEasyEditor window = EditorWindow.GetWindow(EditorPrefs.GetBool("CFX_ShowAsToolbox", true), "CartoonFX Easy Editor", true); window.minSize = new Vector2(300, 8); window.maxSize = new Vector2(300, 8); window.foldoutChanged = true; } //Change Start Color private bool AffectAlpha = true; private Color ColorValue = Color.white; private Color ColorValue2 = Color.white; //Scale private float ScalingValue = 2.0f; private float LTScalingValue = 100.0f; //Delay private float DelayValue = 1.0f; //Duration private float DurationValue = 5.0f; //Tint private bool TintStartColor = true; private bool TintColorModule = true; private bool TintColorSpeedModule = true; private Color TintColorValue = Color.white; //Change Lightness private int LightnessStep = 10; //Module copying private ParticleSystem sourceObject; private Color ColorSelected = new Color(0.8f,0.95f,1.0f,1.0f); private bool[] b_modules = new bool[16]; //Foldouts bool basicFoldout = false; bool colorFoldout = false; bool copyFoldout = false; bool foldoutChanged; //Editor Prefs private bool pref_ShowAsToolbox; private bool pref_IncludeChildren; void OnEnable() { //Load Settings pref_ShowAsToolbox = EditorPrefs.GetBool("CFX_ShowAsToolbox", true); pref_IncludeChildren = EditorPrefs.GetBool("CFX_IncludeChildren", true); basicFoldout = EditorPrefs.GetBool("CFX_BasicFoldout", false); colorFoldout = EditorPrefs.GetBool("CFX_ColorFoldout", false); copyFoldout = EditorPrefs.GetBool("CFX_CopyFoldout", false); } void OnDisable() { //Save Settings EditorPrefs.SetBool("CFX_BasicFoldout", basicFoldout); EditorPrefs.SetBool("CFX_ColorFoldout", colorFoldout); EditorPrefs.SetBool("CFX_CopyFoldout", copyFoldout); } void OnGUI() { GUILayout.BeginArea(new Rect(0,0,this.position.width - 8,this.position.height)); GUILayout.Space(4); GUILayout.BeginHorizontal(); GUILayout.Label("CARTOON FX Easy Editor", EditorStyles.boldLabel); pref_ShowAsToolbox = GUILayout.Toggle(pref_ShowAsToolbox, new GUIContent("Toolbox", "If enabled, the window will be displayed as an external toolbox.\nIf false, it will act as a dockable Unity window."), GUILayout.Width(60)); if(GUI.changed) { EditorPrefs.SetBool("CFX_ShowAsToolbox", pref_ShowAsToolbox); this.Close(); CFXEasyEditor.ShowWindow(); } GUILayout.EndHorizontal(); GUILayout.Label("Easily change properties of any Particle System!", EditorStyles.miniLabel); //---------------------------------------------------------------- pref_IncludeChildren = GUILayout.Toggle(pref_IncludeChildren, new GUIContent("Include Children", "If checked, changes will affect every Particle Systems from each child of the selected GameObject(s)")); if(GUI.changed) { EditorPrefs.SetBool("CFX_IncludeChildren", pref_IncludeChildren); } EditorGUILayout.BeginHorizontal(); GUILayout.Label("Test effect(s):"); if(GUILayout.Button("Play", EditorStyles.miniButtonLeft, GUILayout.Width(50f))) { foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems = go.GetComponents(); if(systems.Length == 0) continue; foreach(ParticleSystem system in systems) system.Play(pref_IncludeChildren); } } if(GUILayout.Button("Pause", EditorStyles.miniButtonMid, GUILayout.Width(50f))) { foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems = go.GetComponents(); if(systems.Length == 0) continue; foreach(ParticleSystem system in systems) system.Pause(pref_IncludeChildren); } } if(GUILayout.Button("Stop", EditorStyles.miniButtonMid, GUILayout.Width(50f))) { foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems = go.GetComponents(); if(systems.Length == 0) continue; foreach(ParticleSystem system in systems) system.Stop(pref_IncludeChildren); } } if(GUILayout.Button("Clear", EditorStyles.miniButtonRight, GUILayout.Width(50f))) { foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems = go.GetComponents(); if(systems.Length == 0) continue; foreach(ParticleSystem system in systems) { system.Stop(pref_IncludeChildren); system.Clear(pref_IncludeChildren); } } } GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); //---------------------------------------------------------------- //Separator GUILayout.Box("",GUILayout.Width(this.position.width - 12), GUILayout.Height(3)); EditorGUI.BeginChangeCheck(); basicFoldout = EditorGUILayout.Foldout(basicFoldout, "QUICK EDIT"); if(EditorGUI.EndChangeCheck()) { foldoutChanged = true; } if(basicFoldout) { //---------------------------------------------------------------- GUILayout.BeginHorizontal(); if(GUILayout.Button(new GUIContent("Scale Size", "Changes the size of the Particle System(s) and other values accordingly (speed, gravity, etc.)"), GUILayout.Width(120))) { applyScale(); } GUILayout.Label("Multiplier:",GUILayout.Width(110)); ScalingValue = EditorGUILayout.FloatField(ScalingValue,GUILayout.Width(50)); if(ScalingValue <= 0) ScalingValue = 0.1f; GUILayout.EndHorizontal(); //---------------------------------------------------------------- GUILayout.BeginHorizontal(); if(GUILayout.Button(new GUIContent("Set Speed", "Changes the speed of the Particle System(s) (if you want quicker or longer effects, 100% = default speed)"), GUILayout.Width(120))) { applySpeed(); } GUILayout.Label("Speed (%):",GUILayout.Width(110)); LTScalingValue = EditorGUILayout.FloatField(LTScalingValue,GUILayout.Width(50)); if(LTScalingValue < 0.1f) LTScalingValue = 0.1f; else if(LTScalingValue > 9999) LTScalingValue = 9999; GUILayout.EndHorizontal(); //---------------------------------------------------------------- GUILayout.BeginHorizontal(); if(GUILayout.Button(new GUIContent("Set Duration", "Changes the duration of the Particle System(s)"), GUILayout.Width(120))) { applyDuration(); } GUILayout.Label("Duration (sec):",GUILayout.Width(110)); DurationValue = EditorGUILayout.FloatField(DurationValue,GUILayout.Width(50)); if(DurationValue < 0.1f) DurationValue = 0.1f; else if(DurationValue > 9999) DurationValue = 9999; GUILayout.EndHorizontal(); //---------------------------------------------------------------- GUILayout.BeginHorizontal(); if(GUILayout.Button(new GUIContent("Set Delay", "Changes the delay of the Particle System(s)"), GUILayout.Width(120))) { applyDelay(); } GUILayout.Label("Delay :",GUILayout.Width(110)); DelayValue = EditorGUILayout.FloatField(DelayValue,GUILayout.Width(50)); if(DelayValue < 0.0f) DelayValue = 0.0f; else if(DelayValue > 9999f) DelayValue = 9999f; GUILayout.EndHorizontal(); //---------------------------------------------------------------- GUILayout.Space(2); GUILayout.BeginHorizontal(); if(GUILayout.Button(new GUIContent("Loop", "Loop the effect (might not work properly on some effects such as explosions)"), EditorStyles.miniButtonLeft)) { loopEffect(true); } if(GUILayout.Button(new GUIContent("Unloop", "Remove looping from the effect"), EditorStyles.miniButtonRight)) { loopEffect(false); } if(GUILayout.Button(new GUIContent("Prewarm On", "Prewarm the effect (if looped)"), EditorStyles.miniButtonLeft)) { prewarmEffect(true); } if(GUILayout.Button(new GUIContent("Prewarm Off", "Don't prewarm the effect (if looped)"), EditorStyles.miniButtonRight)) { prewarmEffect(false); } GUILayout.EndHorizontal(); GUILayout.Space(2); //---------------------------------------------------------------- } //Separator GUILayout.Box("",GUILayout.Width(this.position.width - 12), GUILayout.Height(3)); EditorGUI.BeginChangeCheck(); colorFoldout = EditorGUILayout.Foldout(colorFoldout, "COLOR EDIT"); if(EditorGUI.EndChangeCheck()) { foldoutChanged = true; } if(colorFoldout) { //---------------------------------------------------------------- GUILayout.BeginHorizontal(); if(GUILayout.Button(new GUIContent("Set Start Color(s)", "Changes the color(s) of the Particle System(s)\nSecond Color is used when Start Color is 'Random Between Two Colors'."),GUILayout.Width(120))) { applyColor(); } ColorValue = EditorGUILayout.ColorField(ColorValue); ColorValue2 = EditorGUILayout.ColorField(ColorValue2); AffectAlpha = GUILayout.Toggle(AffectAlpha, new GUIContent("Alpha", "If checked, the alpha value will also be changed")); GUILayout.EndHorizontal(); //---------------------------------------------------------------- GUILayout.BeginHorizontal(); if(GUILayout.Button(new GUIContent("Tint Colors", "Tints the colors of the Particle System(s), including gradients!\n(preserving their saturation and lightness)"),GUILayout.Width(120))) { tintColor(); } TintColorValue = EditorGUILayout.ColorField(TintColorValue); TintColorValue = HSLColor.FromRGBA(TintColorValue).VividColor(); GUILayout.EndHorizontal(); //---------------------------------------------------------------- /* GUILayout.BeginHorizontal(); GUILayout.Label("Add/Substract Lightness:"); LightnessStep = EditorGUILayout.IntField(LightnessStep, GUILayout.Width(30)); if(LightnessStep > 99) LightnessStep = 99; else if(LightnessStep < 1) LightnessStep = 1; GUILayout.Label("%"); if(GUILayout.Button("-", EditorStyles.miniButtonLeft, GUILayout.Width(22))) { addLightness(true); } if(GUILayout.Button("+", EditorStyles.miniButtonRight, GUILayout.Width(22))) { addLightness(false); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); */ //---------------------------------------------------------------- GUILayout.Label("Color Modules to affect:"); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.color = TintStartColor ? ColorSelected : Color.white; if(GUILayout.Button(new GUIContent("Start Color", "If checked, the \"Start Color\" value(s) will be affected."), EditorStyles.toolbarButton, GUILayout.Width(70))) TintStartColor = !TintStartColor; GUI.color = TintColorModule ? ColorSelected : Color.white; if(GUILayout.Button(new GUIContent("Color over Lifetime", "If checked, the \"Color over Lifetime\" value(s) will be affected."), EditorStyles.toolbarButton, GUILayout.Width(110))) TintColorModule = !TintColorModule; GUI.color = TintColorSpeedModule ? ColorSelected : Color.white; if(GUILayout.Button(new GUIContent("Color by Speed", "If checked, the \"Color by Speed\" value(s) will be affected."), EditorStyles.toolbarButton, GUILayout.Width(100))) TintColorSpeedModule = !TintColorSpeedModule; GUI.color = Color.white; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(4); //---------------------------------------------------------------- } //Separator GUILayout.Box("",GUILayout.Width(this.position.width - 12), GUILayout.Height(3)); // GUILayout.Space(6); //---------------------------------------------------------------- EditorGUI.BeginChangeCheck(); copyFoldout = EditorGUILayout.Foldout(copyFoldout, "COPY MODULES"); if(EditorGUI.EndChangeCheck()) { foldoutChanged = true; } if(copyFoldout) { GUILayout.Label("Copy properties from a Particle System to others!", EditorStyles.miniLabel); GUILayout.BeginHorizontal(); GUILayout.Label("Source Object:", GUILayout.Width(110)); sourceObject = (ParticleSystem)EditorGUILayout.ObjectField(sourceObject, typeof(ParticleSystem), true); GUILayout.EndHorizontal(); EditorGUILayout.LabelField("Modules to Copy:"); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if(GUILayout.Button("ALL", EditorStyles.miniButtonLeft, GUILayout.Width(120))) { for(int i = 0; i < b_modules.Length; i++) b_modules[i] = true; } if(GUILayout.Button("NONE", EditorStyles.miniButtonRight, GUILayout.Width(120))) { for(int i = 0; i < b_modules.Length; i++) b_modules[i] = false; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(4); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.color = b_modules[0] ? ColorSelected : Color.white; if(GUILayout.Button("Initial", EditorStyles.toolbarButton, GUILayout.Width(70))) b_modules[0] = !b_modules[0]; GUI.color = b_modules[1] ? ColorSelected : Color.white; if(GUILayout.Button("Emission", EditorStyles.toolbarButton, GUILayout.Width(70))) b_modules[1] = !b_modules[1]; GUI.color = b_modules[2] ? ColorSelected : Color.white; if(GUILayout.Button("Shape", EditorStyles.toolbarButton, GUILayout.Width(70))) b_modules[2] = !b_modules[2]; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.color = b_modules[3] ? ColorSelected : Color.white; if(GUILayout.Button("Velocity", EditorStyles.toolbarButton, GUILayout.Width(70))) b_modules[3] = !b_modules[3]; GUI.color = b_modules[4] ? ColorSelected : Color.white; if(GUILayout.Button("Limit Velocity", EditorStyles.toolbarButton, GUILayout.Width(100))) b_modules[4] = !b_modules[4]; GUI.color = b_modules[5] ? ColorSelected : Color.white; if(GUILayout.Button("Force", EditorStyles.toolbarButton, GUILayout.Width(70))) b_modules[5] = !b_modules[5]; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.color = b_modules[6] ? ColorSelected : Color.white; if(GUILayout.Button("Color over Lifetime", EditorStyles.toolbarButton, GUILayout.Width(120))) b_modules[6] = !b_modules[6]; GUI.color = b_modules[7] ? ColorSelected : Color.white; if(GUILayout.Button("Color by Speed", EditorStyles.toolbarButton, GUILayout.Width(120))) b_modules[7] = !b_modules[7]; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.color = b_modules[8] ? ColorSelected : Color.white; if(GUILayout.Button("Size over Lifetime", EditorStyles.toolbarButton, GUILayout.Width(120))) b_modules[8] = !b_modules[8]; GUI.color = b_modules[9] ? ColorSelected : Color.white; if(GUILayout.Button("Size by Speed", EditorStyles.toolbarButton, GUILayout.Width(120))) b_modules[9] = !b_modules[9]; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.color = b_modules[10] ? ColorSelected : Color.white; if(GUILayout.Button("Rotation over Lifetime", EditorStyles.toolbarButton, GUILayout.Width(120))) b_modules[10] = !b_modules[10]; GUI.color = b_modules[11] ? ColorSelected : Color.white; if(GUILayout.Button("Rotation by Speed", EditorStyles.toolbarButton, GUILayout.Width(120))) b_modules[11] = !b_modules[11]; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.color = b_modules[12] ? ColorSelected : Color.white; if(GUILayout.Button("Collision", EditorStyles.toolbarButton, GUILayout.Width(100))) b_modules[12] = !b_modules[12]; GUI.color = b_modules[13] ? ColorSelected : Color.white; if(GUILayout.Button("Sub Emitters", EditorStyles.toolbarButton, GUILayout.Width(100))) b_modules[13] = !b_modules[13]; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.color = b_modules[14] ? ColorSelected : Color.white; if(GUILayout.Button("Texture Animation", EditorStyles.toolbarButton, GUILayout.Width(110))) b_modules[14] = !b_modules[14]; GUI.color = b_modules[15] ? ColorSelected : Color.white; if(GUILayout.Button("Renderer", EditorStyles.toolbarButton, GUILayout.Width(90))) b_modules[15] = !b_modules[15]; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUI.color = Color.white; GUILayout.Space(4); if(GUILayout.Button("Copy properties to selected Object(s)")) { bool foundPs = false; foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); if(systems.Length == 0) continue; foundPs = true; foreach(ParticleSystem system in systems) CopyModules(sourceObject, system); } if(!foundPs) { Debug.LogWarning("CartoonFX Easy Editor: No Particle System found in the selected GameObject(s)!"); } } } //---------------------------------------------------------------- GUILayout.Space(8); //Resize window if(foldoutChanged && Event.current.type == EventType.Repaint) { foldoutChanged = false; Rect r = GUILayoutUtility.GetLastRect(); this.minSize = new Vector2(300,r.y + 8); this.maxSize = new Vector2(300,r.y + 8); } GUILayout.EndArea(); } //Loop effects private void loopEffect(bool setLoop) { foreach(GameObject go in Selection.gameObjects) { //Scale Shuriken Particles Values ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); foreach(ParticleSystem ps in systems) { SerializedObject so = new SerializedObject(ps); so.FindProperty("looping").boolValue = setLoop; so.ApplyModifiedProperties(); } } } //Prewarm effects private void prewarmEffect(bool setPrewarm) { foreach(GameObject go in Selection.gameObjects) { //Scale Shuriken Particles Values ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); foreach(ParticleSystem ps in systems) { SerializedObject so = new SerializedObject(ps); so.FindProperty("prewarm").boolValue = setPrewarm; so.ApplyModifiedProperties(); } } } //Scale Size private void applyScale() { foreach(GameObject go in Selection.gameObjects) { //Scale Shuriken Particles Values ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); foreach(ParticleSystem ps in systems) { ScaleParticleValues(ps, go); } //Scale Lights' range Light[] lights = go.GetComponentsInChildren(); foreach(Light light in lights) { light.range *= ScalingValue; light.transform.localPosition *= ScalingValue; } } } //Change Color private void applyColor() { foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); foreach(ParticleSystem ps in systems) { SerializedObject psSerial = new SerializedObject(ps); if(!AffectAlpha) { psSerial.FindProperty("InitialModule.startColor.maxColor").colorValue = new Color(ColorValue.r, ColorValue.g, ColorValue.b, psSerial.FindProperty("InitialModule.startColor.maxColor").colorValue.a); psSerial.FindProperty("InitialModule.startColor.minColor").colorValue = new Color(ColorValue2.r, ColorValue2.g, ColorValue2.b, psSerial.FindProperty("InitialModule.startColor.minColor").colorValue.a); } else { psSerial.FindProperty("InitialModule.startColor.maxColor").colorValue = ColorValue; psSerial.FindProperty("InitialModule.startColor.minColor").colorValue = ColorValue2; } psSerial.ApplyModifiedProperties(); } } } //TINT COLORS ================================================================================================================================ private void tintColor() { if(!TintStartColor && !TintColorModule && !TintColorSpeedModule) { Debug.LogWarning("CartoonFX Easy Editor: You must toggle at least one of the three Color Modules to be able to tint anything!"); return; } float hue = HSLColor.FromRGBA(TintColorValue).h; foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); foreach(ParticleSystem ps in systems) { SerializedObject psSerial = new SerializedObject(ps); if(TintStartColor) GenericTintColorProperty(psSerial.FindProperty("InitialModule.startColor"), hue); if(TintColorModule) GenericTintColorProperty(psSerial.FindProperty("ColorModule.gradient"), hue); if(TintColorSpeedModule) GenericTintColorProperty(psSerial.FindProperty("ColorBySpeedModule.gradient"), hue); psSerial.ApplyModifiedProperties(); } } } private void GenericTintColorProperty(SerializedProperty colorProperty, float hue) { int state = colorProperty.FindPropertyRelative("minMaxState").intValue; switch(state) { //Constant Color case 0: colorProperty.FindPropertyRelative("maxColor").colorValue = HSLColor.FromRGBA(colorProperty.FindPropertyRelative("maxColor").colorValue).ColorWithHue(hue); break; //Gradient case 1: TintGradient(colorProperty.FindPropertyRelative("maxGradient"), hue); break; //Random between 2 Colors case 2: colorProperty.FindPropertyRelative("minColor").colorValue = HSLColor.FromRGBA(colorProperty.FindPropertyRelative("minColor").colorValue).ColorWithHue(hue); colorProperty.FindPropertyRelative("maxColor").colorValue = HSLColor.FromRGBA(colorProperty.FindPropertyRelative("maxColor").colorValue).ColorWithHue(hue); break; //Random between 2 Gradients case 3: TintGradient(colorProperty.FindPropertyRelative("maxGradient"), hue); TintGradient(colorProperty.FindPropertyRelative("minGradient"), hue); break; } } private void TintGradient(SerializedProperty gradientProperty, float hue) { gradientProperty.FindPropertyRelative("key0").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key0").colorValue).ColorWithHue(hue); gradientProperty.FindPropertyRelative("key1").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key1").colorValue).ColorWithHue(hue); gradientProperty.FindPropertyRelative("key2").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key2").colorValue).ColorWithHue(hue); gradientProperty.FindPropertyRelative("key3").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key3").colorValue).ColorWithHue(hue); gradientProperty.FindPropertyRelative("key4").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key4").colorValue).ColorWithHue(hue); gradientProperty.FindPropertyRelative("key5").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key5").colorValue).ColorWithHue(hue); gradientProperty.FindPropertyRelative("key6").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key6").colorValue).ColorWithHue(hue); gradientProperty.FindPropertyRelative("key7").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key7").colorValue).ColorWithHue(hue); } //LIGHTNESS OFFSET ================================================================================================================================ private void addLightness(bool substract) { if(!TintStartColor && !TintColorModule && !TintColorSpeedModule) { Debug.LogWarning("CartoonFX Easy Editor: You must toggle at least one of the three Color Modules to be able to change lightness!"); return; } float lightness = (float)(LightnessStep/100f); if(substract) lightness *= -1f; foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); foreach(ParticleSystem ps in systems) { SerializedObject psSerial = new SerializedObject(ps); if(TintStartColor) GenericAddLightness(psSerial.FindProperty("InitialModule.startColor"), lightness); if(TintColorModule) GenericAddLightness(psSerial.FindProperty("ColorModule.gradient"), lightness); if(TintColorSpeedModule) GenericAddLightness(psSerial.FindProperty("ColorBySpeedModule.gradient"), lightness); psSerial.ApplyModifiedProperties(); psSerial.Update(); } } } private void GenericAddLightness(SerializedProperty colorProperty, float lightness) { int state = colorProperty.FindPropertyRelative("minMaxState").intValue; switch(state) { //Constant Color case 0: colorProperty.FindPropertyRelative("maxColor").colorValue = HSLColor.FromRGBA(colorProperty.FindPropertyRelative("maxColor").colorValue).ColorWithLightnessOffset(lightness); break; //Gradient case 1: AddLightnessGradient(colorProperty.FindPropertyRelative("maxGradient"), lightness); break; //Random between 2 Colors case 2: colorProperty.FindPropertyRelative("minColor").colorValue = HSLColor.FromRGBA(colorProperty.FindPropertyRelative("minColor").colorValue).ColorWithLightnessOffset(lightness); colorProperty.FindPropertyRelative("maxColor").colorValue = HSLColor.FromRGBA(colorProperty.FindPropertyRelative("maxColor").colorValue).ColorWithLightnessOffset(lightness); break; //Random between 2 Gradients case 3: AddLightnessGradient(colorProperty.FindPropertyRelative("maxGradient"), lightness); AddLightnessGradient(colorProperty.FindPropertyRelative("minGradient"), lightness); break; } } private void AddLightnessGradient(SerializedProperty gradientProperty, float lightness) { gradientProperty.FindPropertyRelative("key0").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key0").colorValue).ColorWithLightnessOffset(lightness); gradientProperty.FindPropertyRelative("key1").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key1").colorValue).ColorWithLightnessOffset(lightness); gradientProperty.FindPropertyRelative("key2").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key2").colorValue).ColorWithLightnessOffset(lightness); gradientProperty.FindPropertyRelative("key3").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key3").colorValue).ColorWithLightnessOffset(lightness); gradientProperty.FindPropertyRelative("key4").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key4").colorValue).ColorWithLightnessOffset(lightness); gradientProperty.FindPropertyRelative("key5").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key5").colorValue).ColorWithLightnessOffset(lightness); gradientProperty.FindPropertyRelative("key6").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key6").colorValue).ColorWithLightnessOffset(lightness); gradientProperty.FindPropertyRelative("key7").colorValue = HSLColor.FromRGBA(gradientProperty.FindPropertyRelative("key7").colorValue).ColorWithLightnessOffset(lightness); } //RGB / HSL Conversions private struct HSLColor { public float h; public float s; public float l; public float a; public HSLColor(float h, float s, float l, float a) { this.h = h; this.s = s; this.l = l; this.a = a; } public HSLColor(float h, float s, float l) { this.h = h; this.s = s; this.l = l; this.a = 1f; } public HSLColor(Color c) { HSLColor temp = FromRGBA(c); h = temp.h; s = temp.s; l = temp.l; a = temp.a; } public static HSLColor FromRGBA(Color c) { float h, s, l, a; a = c.a; float cmin = Mathf.Min(Mathf.Min(c.r, c.g), c.b); float cmax = Mathf.Max(Mathf.Max(c.r, c.g), c.b); l = (cmin + cmax) / 2f; if (cmin == cmax) { s = 0; h = 0; } else { float delta = cmax - cmin; s = (l <= .5f) ? (delta / (cmax + cmin)) : (delta / (2f - (cmax + cmin))); h = 0; if (c.r == cmax) { h = (c.g - c.b) / delta; } else if (c.g == cmax) { h = 2f + (c.b - c.r) / delta; } else if (c.b == cmax) { h = 4f + (c.r - c.g) / delta; } h = Mathf.Repeat(h * 60f, 360f); } return new HSLColor(h, s, l, a); } public Color ToRGBA() { float r, g, b, a; a = this.a; float m1, m2; m2 = (l <= .5f) ? (l * (1f + s)) : (l + s - l * s); m1 = 2f * l - m2; if (s == 0f) { r = g = b = l; } else { r = Value(m1, m2, h + 120f); g = Value(m1, m2, h); b = Value(m1, m2, h - 120f); } return new Color(r, g, b, a); } static float Value(float n1, float n2, float hue) { hue = Mathf.Repeat(hue, 360f); if (hue < 60f) { return n1 + (n2 - n1) * hue / 60f; } else if (hue < 180f) { return n2; } else if (hue < 240f) { return n1 + (n2 - n1) * (240f - hue) / 60f; } else { return n1; } } public Color VividColor() { this.l = 0.5f; this.s = 1.0f; return this.ToRGBA(); } public Color ColorWithHue(float hue) { this.h = hue; return this.ToRGBA(); } public Color ColorWithLightnessOffset(float lightness) { this.l += lightness; if(this.l > 1.0f) this.l = 1.0f; else if(this.l < 0.0f) this.l = 0.0f; return this.ToRGBA(); } public static implicit operator HSLColor(Color src) { return FromRGBA(src); } public static implicit operator Color(HSLColor src) { return src.ToRGBA(); } } //Scale Lifetime only private void applySpeed() { foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); //Scale Lifetime foreach(ParticleSystem ps in systems) { ps.playbackSpeed = (100.0f/LTScalingValue); } } } //Set Duration private void applyDuration() { foreach(GameObject go in Selection.gameObjects) { //Scale Shuriken Particles Values ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); foreach(ParticleSystem ps in systems) { SerializedObject so = new SerializedObject(ps); so.FindProperty("lengthInSec").floatValue = DurationValue; so.ApplyModifiedProperties(); } } } //Change delay private void applyDelay() { foreach(GameObject go in Selection.gameObjects) { ParticleSystem[] systems; if(pref_IncludeChildren) systems = go.GetComponentsInChildren(true); else systems = go.GetComponents(); //Scale Lifetime foreach(ParticleSystem ps in systems) { ps.startDelay = DelayValue; } } } //Copy Selected Modules private void CopyModules(ParticleSystem source, ParticleSystem dest) { if(source == null) { Debug.LogWarning("CartoonFX Easy Editor: Select a source Particle System to copy properties from first!"); return; } SerializedObject psSource = new SerializedObject(source); SerializedObject psDest = new SerializedObject(dest); //Initial Module if(b_modules[0]) { psDest.FindProperty("prewarm").boolValue = psSource.FindProperty("prewarm").boolValue; psDest.FindProperty("lengthInSec").floatValue = psSource.FindProperty("lengthInSec").floatValue; psDest.FindProperty("moveWithTransform").boolValue = psSource.FindProperty("moveWithTransform").boolValue; GenericModuleCopy(psSource.FindProperty("InitialModule"), psDest.FindProperty("InitialModule")); dest.startDelay = source.startDelay; dest.loop = source.loop; dest.playOnAwake = source.playOnAwake; dest.playbackSpeed = source.playbackSpeed; #if UNITY_5_0 || UNITY_5_1 || UNITY_5_2 dest.emissionRate = source.emissionRate; #endif dest.startSpeed = source.startSpeed; dest.startSize = source.startSize; dest.startColor = source.startColor; dest.startRotation = source.startRotation; dest.startLifetime = source.startLifetime; dest.gravityModifier = source.gravityModifier; } //Emission if(b_modules[1]) GenericModuleCopy(psSource.FindProperty("EmissionModule"), psDest.FindProperty("EmissionModule")); //Shape if(b_modules[2]) GenericModuleCopy(psSource.FindProperty("ShapeModule"), psDest.FindProperty("ShapeModule")); //Velocity if(b_modules[3]) GenericModuleCopy(psSource.FindProperty("VelocityModule"), psDest.FindProperty("VelocityModule")); //Velocity Clamp if(b_modules[4]) GenericModuleCopy(psSource.FindProperty("ClampVelocityModule"), psDest.FindProperty("ClampVelocityModule")); //Force if(b_modules[5]) GenericModuleCopy(psSource.FindProperty("ForceModule"), psDest.FindProperty("ForceModule")); //Color if(b_modules[6]) GenericModuleCopy(psSource.FindProperty("ColorModule"), psDest.FindProperty("ColorModule")); //Color Speed if(b_modules[7]) GenericModuleCopy(psSource.FindProperty("ColorBySpeedModule"), psDest.FindProperty("ColorBySpeedModule")); //Size if(b_modules[8]) GenericModuleCopy(psSource.FindProperty("SizeModule"), psDest.FindProperty("SizeModule")); //Size Speed if(b_modules[9]) GenericModuleCopy(psSource.FindProperty("SizeBySpeedModule"), psDest.FindProperty("SizeBySpeedModule")); //Rotation if(b_modules[10]) GenericModuleCopy(psSource.FindProperty("RotationModule"), psDest.FindProperty("RotationModule")); //Rotation Speed if(b_modules[11]) GenericModuleCopy(psSource.FindProperty("RotationBySpeedModule"), psDest.FindProperty("RotationBySpeedModule")); //Collision if(b_modules[12]) GenericModuleCopy(psSource.FindProperty("CollisionModule"), psDest.FindProperty("CollisionModule")); //Sub Emitters if(b_modules[13]) SubModuleCopy(psSource, psDest); //Texture Animation if(b_modules[14]) GenericModuleCopy(psSource.FindProperty("UVModule"), psDest.FindProperty("UVModule")); //Renderer if(b_modules[15]) { ParticleSystemRenderer rendSource = source.GetComponent(); ParticleSystemRenderer rendDest = dest.GetComponent(); psSource = new SerializedObject(rendSource); psDest = new SerializedObject(rendDest); SerializedProperty ss = psSource.GetIterator(); ss.Next(true); SerializedProperty sd = psDest.GetIterator(); sd.Next(true); GenericModuleCopy(ss, sd, false); } } //Copy One Module's Values private void GenericModuleCopy(SerializedProperty ss, SerializedProperty sd, bool depthBreak = true) { while(true) { //Next Property if(!ss.NextVisible(true)) { break; } sd.NextVisible(true); //If end of module: break if(depthBreak && ss.depth == 0) { break; } bool found = true; switch(ss.propertyType) { case SerializedPropertyType.Boolean : sd.boolValue = ss.boolValue; break; case SerializedPropertyType.Integer : sd.intValue = ss.intValue; break; case SerializedPropertyType.Float : sd.floatValue = ss.floatValue; break; case SerializedPropertyType.Color : sd.colorValue = ss.colorValue; break; case SerializedPropertyType.Bounds : sd.boundsValue = ss.boundsValue; break; case SerializedPropertyType.Enum : sd.enumValueIndex = ss.enumValueIndex; break; case SerializedPropertyType.ObjectReference : sd.objectReferenceValue = ss.objectReferenceValue; break; case SerializedPropertyType.Rect : sd.rectValue = ss.rectValue; break; case SerializedPropertyType.String : sd.stringValue = ss.stringValue; break; case SerializedPropertyType.Vector2 : sd.vector2Value = ss.vector2Value; break; case SerializedPropertyType.Vector3 : sd.vector3Value = ss.vector3Value; break; case SerializedPropertyType.AnimationCurve : sd.animationCurveValue = ss.animationCurveValue; break; #if !UNITY_3_5 case SerializedPropertyType.Gradient : copyGradient(ss,sd); break; #endif default: found = false; break; } if(!found) { found = true; switch(ss.type) { default: found = false; break; } } } //Apply Changes sd.serializedObject.ApplyModifiedProperties(); ss.Dispose(); sd.Dispose(); } #if !UNITY_3_5 private void copyGradient(SerializedProperty ss, SerializedProperty sd) { SerializedProperty gradient = ss.Copy(); SerializedProperty copyGrad = sd.Copy(); gradient.Next(true); copyGrad.Next(true); do { switch(gradient.propertyType) { case SerializedPropertyType.Color: copyGrad.colorValue = gradient.colorValue; break; case SerializedPropertyType.Integer: copyGrad.intValue = gradient.intValue; break; default: Debug.Log("CopyGradient: Unrecognized property type:" + gradient.propertyType); break; } gradient.Next(true); copyGrad.Next(true); } while(gradient.depth > 2); } #endif //Specific Copy for Sub Emitters Module (duplicate Sub Particle Systems) private void SubModuleCopy(SerializedObject source, SerializedObject dest) { dest.FindProperty("SubModule.enabled").boolValue = source.FindProperty("SubModule.enabled").boolValue; GameObject copy; if(source.FindProperty("SubModule.subEmitterBirth").objectReferenceValue != null) { //Duplicate sub Particle Emitter copy = (GameObject)Instantiate((source.FindProperty("SubModule.subEmitterBirth").objectReferenceValue as ParticleSystem).gameObject); //Set as child of destination Vector3 localPos = copy.transform.localPosition; Vector3 localScale = copy.transform.localScale; Vector3 localAngles = copy.transform.localEulerAngles; copy.transform.parent = (dest.targetObject as ParticleSystem).transform; copy.transform.localPosition = localPos; copy.transform.localScale = localScale; copy.transform.localEulerAngles = localAngles; //Assign as sub Particle Emitter dest.FindProperty("SubModule.subEmitterBirth").objectReferenceValue = copy; } if(source.FindProperty("SubModule.subEmitterDeath").objectReferenceValue != null) { //Duplicate sub Particle Emitter copy = (GameObject)Instantiate((source.FindProperty("SubModule.subEmitterDeath").objectReferenceValue as ParticleSystem).gameObject); //Set as child of destination Vector3 localPos = copy.transform.localPosition; Vector3 localScale = copy.transform.localScale; Vector3 localAngles = copy.transform.localEulerAngles; copy.transform.parent = (dest.targetObject as ParticleSystem).transform; copy.transform.localPosition = localPos; copy.transform.localScale = localScale; copy.transform.localEulerAngles = localAngles; //Assign as sub Particle Emitter dest.FindProperty("SubModule.subEmitterDeath").objectReferenceValue = copy; } if(source.FindProperty("SubModule.subEmitterCollision").objectReferenceValue != null) { //Duplicate sub Particle Emitter copy = (GameObject)Instantiate((source.FindProperty("SubModule.subEmitterCollision").objectReferenceValue as ParticleSystem).gameObject); //Set as child of destination Vector3 localPos = copy.transform.localPosition; Vector3 localScale = copy.transform.localScale; Vector3 localAngles = copy.transform.localEulerAngles; copy.transform.parent = (dest.targetObject as ParticleSystem).transform; copy.transform.localPosition = localPos; copy.transform.localScale = localScale; copy.transform.localEulerAngles = localAngles; //Assign as sub Particle Emitter dest.FindProperty("SubModule.subEmitterCollision").objectReferenceValue = copy; } //Apply Changes dest.ApplyModifiedProperties(); } //Scale System private void ScaleParticleValues(ParticleSystem ps, GameObject parent) { //Particle System ps.startSize *= ScalingValue; ps.gravityModifier *= ScalingValue; if(ps.startSpeed > 0.01f) ps.startSpeed *= ScalingValue; if(ps.gameObject != parent) ps.transform.localPosition *= ScalingValue; SerializedObject psSerial = new SerializedObject(ps); //Scale Emission Rate if set on Distance if(psSerial.FindProperty("EmissionModule.enabled").boolValue && psSerial.FindProperty("EmissionModule.m_Type").intValue == 1) { psSerial.FindProperty("EmissionModule.rate.scalar").floatValue /= ScalingValue; } //Scale Size By Speed Module if(psSerial.FindProperty("SizeBySpeedModule.enabled").boolValue) { psSerial.FindProperty("SizeBySpeedModule.range.x").floatValue *= ScalingValue; psSerial.FindProperty("SizeBySpeedModule.range.y").floatValue *= ScalingValue; } //Scale Velocity Module if(psSerial.FindProperty("VelocityModule.enabled").boolValue) { psSerial.FindProperty("VelocityModule.x.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("VelocityModule.x.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("VelocityModule.x.maxCurve").animationCurveValue); psSerial.FindProperty("VelocityModule.y.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("VelocityModule.y.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("VelocityModule.y.maxCurve").animationCurveValue); psSerial.FindProperty("VelocityModule.z.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("VelocityModule.z.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("VelocityModule.z.maxCurve").animationCurveValue); } //Scale Limit Velocity Module if(psSerial.FindProperty("ClampVelocityModule.enabled").boolValue) { psSerial.FindProperty("ClampVelocityModule.x.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("ClampVelocityModule.x.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("ClampVelocityModule.x.maxCurve").animationCurveValue); psSerial.FindProperty("ClampVelocityModule.y.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("ClampVelocityModule.y.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("ClampVelocityModule.y.maxCurve").animationCurveValue); psSerial.FindProperty("ClampVelocityModule.z.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("ClampVelocityModule.z.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("ClampVelocityModule.z.maxCurve").animationCurveValue); psSerial.FindProperty("ClampVelocityModule.magnitude.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("ClampVelocityModule.magnitude.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("ClampVelocityModule.magnitude.maxCurve").animationCurveValue); } //Scale Force Module if(psSerial.FindProperty("ForceModule.enabled").boolValue) { psSerial.FindProperty("ForceModule.x.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("ForceModule.x.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("ForceModule.x.maxCurve").animationCurveValue); psSerial.FindProperty("ForceModule.y.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("ForceModule.y.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("ForceModule.y.maxCurve").animationCurveValue); psSerial.FindProperty("ForceModule.z.scalar").floatValue *= ScalingValue; IterateKeys(psSerial.FindProperty("ForceModule.z.minCurve").animationCurveValue); IterateKeys(psSerial.FindProperty("ForceModule.z.maxCurve").animationCurveValue); } //Scale Shape Module if(psSerial.FindProperty("ShapeModule.enabled").boolValue) { psSerial.FindProperty("ShapeModule.boxX").floatValue *= ScalingValue; psSerial.FindProperty("ShapeModule.boxY").floatValue *= ScalingValue; psSerial.FindProperty("ShapeModule.boxZ").floatValue *= ScalingValue; psSerial.FindProperty("ShapeModule.radius").floatValue *= ScalingValue; //Create a new scaled Mesh if there is a Mesh reference //(ShapeModule.type 6 == Mesh) if(psSerial.FindProperty("ShapeModule.type").intValue == 6) { #if !UNITY_3_5 //Unity 4+ : changing the Transform scale will affect the shape Mesh ps.transform.localScale = ps.transform.localScale * ScalingValue; EditorUtility.SetDirty(ps); #else Object obj = psSerial.FindProperty("ShapeModule.m_Mesh").objectReferenceValue; if(obj != null) { Mesh mesh = (Mesh)obj; string assetPath = AssetDatabase.GetAssetPath(mesh); string name = assetPath.Substring(assetPath.LastIndexOf("/")+1); //Mesh to use Mesh meshToUse = null; bool createScaledMesh = true; float meshScale = ScalingValue; //Mesh has already been scaled: extract scaling value and re-scale base effect if(name.Contains("(scaled)")) { string scaleStr = name.Substring(name.LastIndexOf("x")+1); scaleStr = scaleStr.Remove(scaleStr.IndexOf(" (scaled).asset")); float oldScale = float.Parse(scaleStr); if(oldScale != 0) { meshScale = oldScale * ScalingValue; //Check if there's already a mesh with the correct scale string unscaledName = assetPath.Substring(0, assetPath.LastIndexOf(" x")); assetPath = unscaledName; string newPath = assetPath + " x"+meshScale+" (scaled).asset"; Mesh alreadyScaledMesh = (Mesh)AssetDatabase.LoadAssetAtPath(newPath, typeof(Mesh)); if(alreadyScaledMesh != null) { meshToUse = alreadyScaledMesh; createScaledMesh = false; } else //Load original unscaled mesh { Mesh orgMesh = (Mesh)AssetDatabase.LoadAssetAtPath(assetPath, typeof(Mesh)); if(orgMesh != null) { mesh = orgMesh; } } } } else //Verify if original mesh has already been scaled to that value { string newPath = assetPath + " x"+meshScale+" (scaled).asset"; Mesh alreadyScaledMesh = (Mesh)AssetDatabase.LoadAssetAtPath(newPath, typeof(Mesh)); if(alreadyScaledMesh != null) { meshToUse = alreadyScaledMesh; createScaledMesh = false; } } //Duplicate and scale mesh vertices if necessary if(createScaledMesh) { string newMeshPath = assetPath + " x"+meshScale+" (scaled).asset"; meshToUse = (Mesh)AssetDatabase.LoadAssetAtPath(newMeshPath, typeof(Mesh)); if(meshToUse == null) { meshToUse = DuplicateAndScaleMesh(mesh, meshScale); AssetDatabase.CreateAsset(meshToUse, newMeshPath); } } //Apply new Mesh psSerial.FindProperty("ShapeModule.m_Mesh").objectReferenceValue = meshToUse; } #endif } } //Apply Modified Properties psSerial.ApplyModifiedProperties(); } //Iterate and Scale Keys (Animation Curve) private void IterateKeys(AnimationCurve curve) { for(int i = 0; i < curve.keys.Length; i++) { curve.keys[i].value *= ScalingValue; } } //Create Scaled Mesh private Mesh DuplicateAndScaleMesh(Mesh mesh, float Scale) { Mesh scaledMesh = new Mesh(); Vector3[] scaledVertices = new Vector3[mesh.vertices.Length]; for(int i = 0; i < scaledVertices.Length; i++) { scaledVertices[i] = mesh.vertices[i] * Scale; } scaledMesh.vertices = scaledVertices; scaledMesh.normals = mesh.normals; scaledMesh.tangents = mesh.tangents; scaledMesh.triangles = mesh.triangles; scaledMesh.uv = mesh.uv; scaledMesh.uv2 = mesh.uv2; scaledMesh.colors = mesh.colors; return scaledMesh; } }