- Home /
How to set an object reference field in the Inspector
What I want is to be able to call a method on an object (doesn't need to be MonoBehaviour), where the specific implementation of that method is chosen in the inspector. I will paste an example of what I'm trying to do, then describe in better detail how I would like it to work.
The Base Class (Could also be an Interface, Abstract class... whatever works.)
[System.Serializable]
public class TestBase
{
public virtual string RetStr()
{
return "Base Class";
}
}
The inherited class/Implementer:
[System.Serializable]
public class TestClass : TestBase
{
public override string RetStr()
{
return "Inherited Class";
}
}
The MonoBehaviour, with the public field I wish to set:
using UnityEngine;
public class Assignee : MonoBehaviour {
public TestBase testObj;
private void Awake()
{
if (testObj == null)
Debug.Log("testObj is null");
else
Debug.Log("testObj.RetStr() = " + testObj.RetStr());
}
}
And the custom editor (that, to keep it simple, Assigns either the base or the inherited):
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Assignee))]
public class AssigneeEditor : Editor
{
Assignee assignee;
bool setTest;
void OnEnable()
{
assignee = target as Assignee;
setTest = assignee.testObj.GetType() != typeof(TestBase);
}
public override void OnInspectorGUI()
{
setTest = EditorGUILayout.Toggle(new GUIContent("Set testObj"), setTest);
if (setTest)
assignee.testObj = new TestClass();
else
assignee.testObj = new TestBase();
serializedObject.ApplyModifiedProperties();
}
}
The issue is that, while I can change the field in play mode, entering/exiting play mode resets it, also that this whole method is weird. This is probably due to it not being serialized.
The goal is for the workflow to be like: Create new C# script -> Change it to inherit from/implement TestBase -> On a MonoBehaviour component that has a TestBase field, choose a specific one somehow (for now, just selecting between two is sufficient to prove it can be done).
the problem is that you're assigning an actual instance, which of course is only alive at runtime. is it was a monobehaviour in the scene, unity would manage the instantiation through the serialization system for you, but it's not, you need to do that for yourself. now since you can't save an instance, what can you save. you could go for the concrete type, but System.Type is not serializable. what you can serialize though, is the name. to uniquely identify it, the assembly is also needed.
so what you need to do is populate a dropdown showing all thr derivates from your base type. you can use reflection for that. then you save the full name, including the assembly, as a string for which you wrote the editor script. I went down this road and decided to do this via custom inspector which uses an attribute to define the base type you want to serialize the derivates into the string.
one pitfall comes in when deciding to change to or from Assembly definition files as that switch changes the assembly rendering all serializations useless, unless you write code that compensates for that.
I did have a working method to get all System.Types that implement, but yes, I found out that System.Types are also not serializable. Saving the full name is an interesting idea though! Seems like it would work, as I could just instantiate it in Awake() based on a string. I'm certain that would work.
The original reason for this question was because my "Assignee" was a ScritptableObject, not a $$anonymous$$onoBehaviour. After asking this question, I realized I could simply make both Assignee and TestBase $$anonymous$$onoBehaviours and add them both as components, setting up the ref with GetComponent<>(). This isn't an ideal solution either, as the objects really don't need any $$anonymous$$onobehaviour features, but it seems to be what Unity wants, and avoids any weird stuff with reflection.
Thanks a lot for the solution though! I'll keep that in $$anonymous$$d.