- Home /
Expose Private Field in Custom Editor
I am writing a custom editor for one of my scripts. The script has private fields that I want to expose to the editor. I want to keep them private as I don't want them to be able to be editable by other classes. I have used [SerializeField] to expose private variables in the standard editor, but I don't know how to expose them in a custom editor.
Class:
public class Foo : MonoBehaviour
{
[SerializeField] private int bar;
}
CustomEditor:
[CustomEditor(typeof(Foo))]
public class EditorFoo : Editor
{
public override void OnInspectorGUI()
{
Foo script = (Foo)target;
script.bar = EditorGUILayout.IntField("Bar:" script.bar); // Bar is inaccessible due to its protection level
);
}
}
Answer by Bunny83 · Sep 24, 2017 at 12:45 AM
Well, there are generally 3 different ways:
Use the SerializedObject / SerializedProperty mechanics instead of using the "target" field. This is the recommended solution
Add some sort of public property or getter / setter to your class so the editor can actually access it. This of course indirectly makes the field editable by others.
Another way would be to use reflection to edit the private member field.
If it's important for you that the field should not be exposed (directly or indirectly) the second solution is out. If you insist in using the old way to write custom inspectors (using "target" or "targets") the only way to change private or protected members is to use reflection.
However the recommended way would be to use the serializedObject of the Editor. You would use FindProperty to get a SerializedProperty back. This can be used to modify the serialized data of that field.
The SerializedObject / SerializedProperty is actually a wrapper for the serialized data that Unity's serializer has stored on the native side. It's also used by the default inspector. Always keep in mind that the SerializedObject and SerializedProperty do not actually work on the class instance, but directly on the serialized data. That's why the usage might seem a bit strange at first glance.
You may want to have a closer look at the first example on the Editor page
I went with the SerializedObject/Property solution, this does exactly what I need it to. Took me a while to get it to work because I was forgetting to Apply$$anonymous$$odifiedProperties.
Would you still recommend using SerializedObject / SerializedProperty mechanics within a function like OnSceneGUI
? I ask because the docs say (my emphasis):
"Do not use the serializedObject inside OnSceneGUI or OnPreviewGUI. Use the target property directly in those callback functions instead."
So in that case, what's the next best alternative?
Answer by MaxGuernseyIII · Sep 23, 2017 at 11:28 PM
Private members are accessible to other members of the same class, including inner classes.
using UnityEditor;
using UnityEngine;
public class Foo : MonoBehaviour
{
private int bar;
[CustomEditor(typeof(Foo))]
public class EditorFoo : Editor
{
public override void OnInspectorGUI()
{
var script = (Foo)target;
script.bar = EditorGUILayout.IntField("Bar:", script.bar);
}
}
}
This doesn't make much sense as the editor script need to:
be placed in a folder called editor while the runtime script must not be in such a folder.
and a custom inspector (class derived from Editor) also need to have the filename match the classname since the Editor class is actually derived from ScriptableObject
Rather than go by whatever you used to come up with those rules, I did something bizarre. I tested the code, saw that it actually worked, and posted my answer.
I don't think the Editor folder rule has been true for ages and I don't know what makes you think that the editor class needs to be in a file with a name that matches the class name. Are you a java developer, maybe?
Well, have you tried building your project?
Unity's serialization of the Editor instance will break if the file name doesn't match the classname. That's a general rule for any class derived from $$anonymous$$onoBehaviour or ScriptableObject.
The only exception is when you compile your class manually into an assembly and you're using that assembly in the project. However as far as i know the class still can't be a nested class.
ps: No, i'm not a java developer. And yes, i do sometimes group multiple classes in a single file, but not $$anonymous$$onoBehaviours or ScriptableObjects. This includes Editor and EditorWindows as well.
Answer by ZackOfAllTrades · Jun 12, 2020 at 08:30 PM
Another hot tip is make the class partial so your editor script can be in a different file.
// in file named Foo.cs
public partial class Foo : MonoBehaviour
{
private int bar;
}
// in a different file name Foo.Editor.cs
public partial class Foo
{
[CustomEditor(typeof(Foo))]
public class FooEditor: Editor
{
public override void OnInspectorGUI()
{
var script = (Foo)target;
script.bar = EditorGUILayout.IntField("Bar:", script.bar);
}
}
}
I think this is a great solution. I use it too. Note if you use assembly definitions then the editor file needs to be in the same assembly as the monobehaviour. I believe you can still have the editor file in an editor folder in the same assemble as the MonoBehaviour and everything will build, but I haven't tested all that.
Your answer
