How to have a gradient editor in an editor script?

I was making an editor script where you have to define a gradient, and I noticed there’s a gradient editor in the particle system editor:
http://docs.unity3d.com/Documentation/Manual/GradientEditor.html
and there’s also the gradient class you can access from script:

So is there a way to have this in an editor script?
I mean something like EditorGUILayout.GradientField
(like there is EditorGUILayout.ColorField)

In Unity 3.5.7 this functionality is provided entirely by the editor API (whilst in Unity 4.x the Gradient data class is available from the runtime library).

Here is a quick reflection based hack which I have quickly put together for you which allows you to make use of the gradient editor field in both Unity 3.5.7 and Unity 4.x. To make this easier to use I have created a wrapper, but this functionality is strictly editor-only.

[10291-screenyt.png*_|10291]

I would recommend adding error handling to the reflection side of things, this was literally just a quick mashup to demonstrate the concept which I mentioned in my earlier comment. I hope that this will be of some help to you :slight_smile:

Side Note: It probably be relatively easy to replicate this with an entirely custom editor field since you could probably just use vertex colors to simulate the gradient blending.

Assets/Editor/GradientTest/GUIGradientField.cs

using UnityEngine;
using UnityEditor;

using System.Reflection;

using Type = System.Type;

public static class GUIGradientField {
	
	#region Initial Setup
	
	private static MethodInfo s_miGradientField1;
	private static MethodInfo s_miGradientField2;
	
	static GUIGradientField() {
		// Get our grubby hands on hidden "GradientField" :)
		Type tyEditorGUILayout = typeof(EditorGUILayout);
		s_miGradientField1 = tyEditorGUILayout.GetMethod("GradientField", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string), GradientWrapper.s_tyGradient, typeof(GUILayoutOption[]) }, null);
		s_miGradientField2 = tyEditorGUILayout.GetMethod("GradientField", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { GradientWrapper.s_tyGradient, typeof(GUILayoutOption[]) }, null);
	}
	
	#endregion
	
	public static GradientWrapper GradientField(string label, GradientWrapper gradient, params GUILayoutOption[] options) {
		if (gradient == null)
			gradient = new GradientWrapper();

		gradient.GradientData = s_miGradientField1.Invoke(null, new object[] { label, gradient.GradientData, options });
		
		return gradient;
	}
	
	public static GradientWrapper GradientField(GradientWrapper gradient, params GUILayoutOption[] options) {
		if (gradient == null)
			gradient = new GradientWrapper();

		gradient.GradientData = s_miGradientField1.Invoke(null, new object[] { gradient.GradientData, options });
		
		return gradient;
	}
	
}

Assets/Editor/GradientTest/GradientWrapper.cs

using UnityEngine;
using UnityEditor;

using System.Reflection;
using System.Linq;

using Activator = System.Activator;
using Array = System.Array;
using Type = System.Type;

[System.Serializable]
public class GradientWrapper {
	
	/// <summary>
	/// Wrapper for <c>GradientColorKey</c>.
	/// </summary>
	public struct ColorKey {
		public Color color;
		public float time;
		
		public ColorKey(Color color, float time) {
			this.color = color;
			this.time = time;
		}
	}

	/// <summary>
	/// Wrapper for <c>GradientAlphaKey</c>.
	/// </summary>
	public struct AlphaKey {
		public float alpha;
		public float time;
		
		public AlphaKey(float alpha, float time) {
			this.alpha = alpha;
			this.time = time;
		}
	}
	
	#region Initial Setup
	
	/// <summary>
	/// Type of gradient.
	/// </summary>
	public static Type s_tyGradient;
	
#if (UNITY_3_5 || UNITY_3_6 || UNITY_3_7 || UNITY_3_8 || UNITY_3_9)
	private static MethodInfo s_miEvaluate;
	private static PropertyInfo s_piColorKeys;
	private static PropertyInfo s_piAlphaKeys;

	private static Type s_tyGradientColorKey;
	private static Type s_tyGradientAlphaKey;
#endif

	/// <summary>
	/// Perform one-off setup when class is accessed for first time.
	/// </summary>
	static GradientWrapper() {
#if (UNITY_3_5 || UNITY_3_6 || UNITY_3_7 || UNITY_3_8 || UNITY_3_9)	
		Assembly editorAssembly = typeof(Editor).Assembly;

		s_tyGradientColorKey = editorAssembly.GetType("UnityEditor.GradientColorKey");
		s_tyGradientAlphaKey = editorAssembly.GetType("UnityEditor.GradientAlphaKey");

		// Note that `Gradient` is defined in the editor namespace in Unity 3.5.7!
		s_tyGradient = editorAssembly.GetType("UnityEditor.Gradient");
		s_miEvaluate = s_tyGradient.GetMethod("CalcColor", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(float) }, null);
		s_piColorKeys = s_tyGradient.GetProperty("colorKeys", BindingFlags.Public | BindingFlags.Instance);
		s_piAlphaKeys = s_tyGradient.GetProperty("alphaKeys", BindingFlags.Public | BindingFlags.Instance);
#else
		// In Unity 4 this is easy :)
		s_tyGradient = typeof(Gradient);
#endif
	}
	
	#endregion
	
#if (UNITY_3_5 || UNITY_3_6 || UNITY_3_7 || UNITY_3_8 || UNITY_3_9)	
	#region Unity 3.5.7 Implementation
	
	private object _gradient = Activator.CreateInstance(s_tyGradient);
	
	public object GradientData {
		get { return _gradient; }
		set { _gradient = value; }
	}
	
	public Color Evaluate(float time) {
		return (Color)s_miEvaluate.Invoke(_gradient, new object[] { time });
	}
	
	public void SetKeys(ColorKey[] colorKeys, AlphaKey[] alphaKeys) {
		if (colorKeys != null) {
			Array colorKeyParam = (Array)Activator.CreateInstance(s_tyGradientColorKey.MakeArrayType(), new object[] { colorKeys.Length });
			for (int i = 0; i < colorKeys.Length; ++i)
				colorKeyParam.SetValue(Activator.CreateInstance(s_tyGradientColorKey, colorKeys_.color, colorKeys*.time), i);*_

* s_piColorKeys.SetValue(gradient, colorKeyParam, null);
_
}*

* if (alphaKeys != null) {*
* Array alphaKeyParam = (Array)Activator.CreateInstance(s_tyGradientAlphaKey.MakeArrayType(), new object { alphaKeys.Length });
_
for (int i = 0; i < alphaKeys.Length; ++i)_
alphaKeyParam.SetValue(Activator.CreateInstance(s_tyGradientAlphaKey, alphaKeys_.alpha, alphaKeys.time), i);_
s_piAlphaKeys.SetValue(gradient, alphaKeyParam, null);*
* }
}*_

* #endregion*
#else
* #region Unity 4.x Implementation*

* private Gradient _gradient = new Gradient();*

* public object GradientData {*
* get { return gradient; }
set { gradient = value as Gradient; }*
* }*

* public Color Evaluate(float time) {*
* return gradient.Evaluate(time);*
* }*_

* public void SetKeys(ColorKey[] colorKeys, AlphaKey[] alphaKeys) {*
* GradientColorKey[] actualColorKeys = null;*
* GradientAlphaKey[] actualAlphaKeys = null;*

* if (colorKeys != null)*
* actualColorKeys = colorKeys.Select(key => new GradientColorKey(key.color, key.time)).ToArray();*
* if (alphaKeys != null)*
* actualAlphaKeys = alphaKeys.Select(key => new GradientAlphaKey(key.alpha, key.time)).ToArray();*

* gradient.SetKeys(actualColorKeys, actualAlphaKeys);*
* }*_

* #endregion*
#endif

}
### Assets/Editor/GradientTest/GradientEditorWindow.cs
using UnityEngine;
using UnityEditor;

public class GradientEditorWindow : EditorWindow {

* [MenuItem(“Window/Gradient Editor Test”)]*
* public static void Show() {*
* GetWindow(“Gradient Editor Test Window”);*
* }*

* GradientWrapper gradient;*
* float time;*

* GUIStyle previewStyle;*

* void OnEnable() {*
* previewStyle = new GUIStyle();*
* previewStyle.normal.background = EditorGUIUtility.whiteTexture;*
* }*

* void OnGUI() {*
* gradient = GUIGradientField.GradientField(“Test Gradient”, gradient);*

* if (GUILayout.Button(“Set Keys Test”)) {*
* OnSetKeysTest();*
* GUIUtility.ExitGUI();*
* }*

* time = Mathf.Clamp01(EditorGUILayout.Slider(“Time”, time, 0, 1));*

* // Extract color at specific time.*
* Color restoreBackgroundColor = GUI.backgroundColor;*
* GUI.backgroundColor = gradient.Evaluate(time);*
* GUILayout.Box(GUIContent.none, previewStyle, GUILayout.Width(200), GUILayout.Height(50));*
* GUI.backgroundColor = restoreBackgroundColor;*
* }*

* void OnSetKeysTest() {*
* GradientWrapper.ColorKey[] colorKeys = new GradientWrapper.ColorKey[] {*
* new GradientWrapper.ColorKey(Color.red, 0f),*
* new GradientWrapper.ColorKey(Color.green, 0.5f),*
* new GradientWrapper.ColorKey(Color.blue, 1f)*
* };*

* GradientWrapper.AlphaKey[] alphaKeys = new GradientWrapper.AlphaKey[] {*
* new GradientWrapper.AlphaKey(1f, 0f),*
* new GradientWrapper.AlphaKey(1f, 1f)*
* };*

* gradient.SetKeys(colorKeys, alphaKeys);*
* }*

}
_*

I found a simpler way. At least less code.

Use EditorGUILayout.PropertyField and pass in a serializedProperty of a Gradient.

In my case I created a class that has a public gradient property. Example of some pseudo code below. Note, you will need to create a GradientContainer class that extends MonoBehaviour and has a public Gradient variable named ‘ColorGradient’.

// OnEnable 
GameObject tempObject = new GameObject();

GradientContainer gradientContainer = (GradientContainer) tempObject.AddComponent("GradientContainer");

SerializedObject serializedGradient = new SerializedObject(gradientContainer);
SerializedProperty colorGradient = serializedGradient.FindProperty("ColorGradient");

// OnGUI
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(colorGradient, true, null);
if(EditorGUI.EndChangeCheck())
{
	serializedGradient.ApplyModifiedProperties();
}

// Accessing Later
GradientContainer gradientContainer = (GradientContainer)serializedGradient.targetObject;

To have the gradient editor appear for a property of your script, just declare it as:

C#:

public Gradient myGradient;

JS:

myGradient:Gradient;

Then you can access it via script as per the documentation you already found.

How exactly does one implement ickydime’s solution?

I have tried to follow what he describes but have been unable to get the pseudocode to a usable state.

//using System.Linq //using System.Reflection
var method = typeof(EditorGUI)
    .GetMethods(BindingFlags.NonPublic |BindingFlags.Static )
    .First(t=>t.Name == "GradientField");
var change = m.Invoke(null, new object[] { rect, gradient });

it’s working anyway.