I don’t have much expirence with making custom inspectors so I wanted to ask for some help please!
I have a prefab that is already used extensively in my project. I want to modify the main script on this prefab to add a custom button in the inspector for future functionality. The button will let the dev optionally add an Image (Texture2D) and ImageText (String). I don’t want to mess up the existing fields that are already there as that could potentially undo a lot of work that that has already been done in the project.
This is the class already on the prefab (obviously with it’s actual name changed and unnecessary code removed):
public class MyScript : MonoBehaviour
{
[SerializeField] public Texture2D Image { get; set; }
[SerializeField] public string ImageText { get; set; }
public bool AddImage { get; set; } = true;
<truncated> . . .
This is my custom editor script:
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(MyScript))]
class MyScriptEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
var myScript = (MyScript)target;
if (myScript == null) return;
Undo.RecordObject(myScript , "Undo Add Image");
if (!myScript.AddImage)
{
if (GUILayout.Button("Add Image"))
{
myScript.AddImage = true;
}
}
if (myScript.AddImage)
{
if (GUILayout.Button("Remove Image"))
{
myScript.AddImage = false;
}
myScript.Image = EditorGUILayout.ObjectField("Step Image", myScript.Image, typeof(Texture2D), false) as Texture2D;
myScript.ImageText = EditorGUILayout.TextField(myScript.ImageText);
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
Here is what it produces. A new button at the bottom of my inspector:
The button when clicked:
At first blush this appears to be what I want. The problem is that in testing, (used the button to add an image and text), it’s not actually saving the property values when I hit play?
@Bunny83 & @Namey5 - Hope you don’t mind if I tag you 2 as ya’ll are my favorite people on Unity answers.
Okay I’ve managed to get it working but not sure if this is the best way or not. I’ve modified my code as follows:
The MyScript class:
public class MyScript : MonoBehaviour
{
public Texture2D Image { get { return m_Image; } set { m_Image = value; } }
[HideInInspector] [SerializeField] private Texture2D m_Image = null;
public string ImageText { get { return m_ImageText; } set { m_ImageText = value; } }
[HideInInspector] [SerializeField] private string m_ImageText = "";
public bool AddImage { get { return m_AddImage; } set { m_AddImage = value; } }
[HideInInspector] [SerializeField] private bool m_AddImage = false;
<truncated>
.
.
.
And the MyScriptEditor class :
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(MyScript))]
class MyScriptEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
var myScript = (myScript)target;
if (myScript == null) return;
Undo.RecordObject(myScript, "Undo Add Image");
if (!myScript.AddImage)
{
if (GUILayout.Button("Add Image"))
{
serializedObject.FindProperty("m_AddImage").boolValue = true;
}
}
if (myScript.AddImage)
{
if (GUILayout.Button("Remove Image"))
{
serializedObject.FindProperty("m_AddImage").boolValue = false;
}
serializedObject.FindProperty("m_Image").objectReferenceValue = EditorGUILayout.ObjectField("Step Image", myScript.Image, typeof(Texture2D), false) as Texture2D;
serializedObject.FindProperty("m_ImageText").stringValue = EditorGUILayout.TextField("Image Text", myScript.ImageText);
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
And it is now keeping the set Texture2D and Text when I hit Play.
There’s probably a better way to do this so that I don’t have to use the [HideInInspector] attribute?
Properties are basically just shorthand functions, so serialising them can be pretty ambiguous (even auto properties like in your original example) - because they can’t be serialised, Unity has no way of saving their values between contexts as they don’t really actually have values. Considering these are public auto-properties, is there even a reason you really need them to be properties at all? If they were regular public fields with the HideInInspector attribute you could access them in the same way, they wouldn’t be displayed by base.OnInspectorGUI and their data would be stored properly;
public class MyScript : MonoBehaviour
{
[HideInInspector] public Texture2D Image;
[HideInInspector] public string ImageText;
[HideInInspector] public bool AddImage = true;
...
[CustomEditor(typeof(MyScript))]
class MyScriptEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
var myScript = (MyScript)target;
if (myScript == null) return;
Undo.RecordObject(myScript , "Undo Add Image");
if (!myScript.AddImage)
{
if (GUILayout.Button("Add Image"))
{
myScript.AddImage = true;
}
}
else
{
if (GUILayout.Button("Remove Image"))
{
myScript.AddImage = false;
}
myScript.Image = EditorGUILayout.ObjectField("Step Image", myScript.Image, typeof(Texture2D), false) as Texture2D;
myScript.ImageText = EditorGUILayout.TextField(myScript.ImageText);
}
serializedObject.ApplyModifiedProperties();
}
}