I have a ScriptableObject that simply have a list of object:
class MyClassHolder : ScriptableObject
{
public MyClass[] myClass;
}
[System.Serializable]
public class MyClass
{
public string key;
public int priority;
public Data data;
}
And some class for datas:
[System.Serializable]
public class Data
{
public Icon icon;
public Color color;
}
[System.Serializable]
public class Icon
{
public TextureSet[] small;
public TextureSet[] big;
}
[System.Serializable]
public class TextureSet
{
public Texture2D texture;
public Vector2 offset;
[Range(0f, 1.2f)]
public float scale = 1;
}
Which now looking like this:
I have the code for drawing the icon which just like:
What i want to try to do is add a preview beside of the "Small" and "Big", like this if can:
Or under the Data like this:
I know there are CustomEditor or some Drawer but no idea which is suite for the case.
And Which class should i working with?
Update: I end up having this, looking good enter image description here
For this use case I wouldn't use a CustomEditor, which is for implementing a custom Inspector for an entire ScriptableObject
or MonoBehaviour
. This would be a lot of overhead just to customize the way a certain field is drawn. Additionally you would have to do it for each and every other class where you use this field type.
You rather want a custom PropertyDrawer
, which is used to implement a custom way for drawing only the field of a specific type - in your case for the TextureSet
.
Could look somewhat like e.g.
...
#if UNITY_EDITOR
using UnityEditor;
#endif
[Serializable]
public class TextureSet
{
public Texture2D texture;
public Vector2 offset;
[UnityEngine.Range(0f, 1.2f)]
public float scale = 1;
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(TextureSet))]
private class TextureSetDrawer : PropertyDrawer
{
// height and width of the preview field in lines
const float PREVIEW_SIZE_LINES = 4;
// height and width of the preview field in pixels
readonly float PREVIEW_SIZE = EditorGUIUtility.singleLineHeight * PREVIEW_SIZE_LINES;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if (!property.isExpanded)
{
// if folded simply single line
return EditorGUIUtility.singleLineHeight;
}
// compose the total height of the poperty
// 1 line - offset
// 1 line - scale
// PREVIEW_SIZE_LINES - preview
// 1 line - a bit of buffer to separate from next list element
return EditorGUIUtility.singleLineHeight * (2 + PREVIEW_SIZE_LINES + 1);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
using (new EditorGUI.PropertyScope(position, label, property))
{
if (!property.isExpanded)
{
// draw the foldout label of the entire TextureSet property
property.isExpanded = EditorGUI.Foldout(position, property.isExpanded, label);
}
else
{
// move down half buffer to separate a bit from previous fields
position.y += EditorGUIUtility.singleLineHeight * 0.5f;
// draw the foldout label of the entire TextureSet property
property.isExpanded = EditorGUI.Foldout(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), property.isExpanded, label);
// indent the child properties a bit for better visual grouping
using (new EditorGUI.IndentLevelScope())
{
position = EditorGUI.IndentedRect(position);
// Find/Bind the three properties
var textureProperty = property.FindPropertyRelative(nameof(texture));
var offsetProperty = property.FindPropertyRelative(nameof(offset));
var scaleProperty = property.FindPropertyRelative(nameof(scale));
// Calculate the positions and sizes of the fields to draw
var textureRect = new Rect(position.x, position.y + PREVIEW_SIZE * 0.5f - EditorGUIUtility.singleLineHeight * 0.5f, position.width - PREVIEW_SIZE, EditorGUIUtility.singleLineHeight);
var previewRect = new Rect(position.x + position.width - PREVIEW_SIZE, position.y, PREVIEW_SIZE, PREVIEW_SIZE);
var offsetRect = new Rect(position.x, position.y + previewRect.height, position.width, EditorGUIUtility.singleLineHeight);
var scaleRect = new Rect(position.x, offsetRect.y + EditorGUIUtility.singleLineHeight, position.width, EditorGUIUtility.singleLineHeight);
// The default texture field
EditorGUI.PropertyField(textureRect, textureProperty);
// using a grey texture as fallback if there is none referenced yet
var tex = textureProperty.objectReferenceValue ? (Texture)textureProperty.objectReferenceValue : Texture2D.grayTexture;
var texCoords = new Rect(offsetProperty.vector2Value.x, offsetProperty.vector2Value.y, 1 / scaleProperty.floatValue, 1 / scaleProperty.floatValue);
GUI.DrawTextureWithTexCoords(previewRect, tex, texCoords);
// The default vector2 field
EditorGUI.PropertyField(offsetRect, offsetProperty);
// The default float field with RangeAttribute applied
EditorGUI.PropertyField(scaleRect, scaleProperty);
}
}
}
}
}
#endif
}
I hope this is a good starting point, you will probably have to play around a bit and figure out how exactly you want to apply the scale
and offset
to the preview according to your needs.
Little demo
public class Example : MonoBehaviour
{
public TextureSet test;
public TextureSet[] tests;
}