2
\$\begingroup\$

For all my MonoBehaviour scripts, instead of them inheriting from MonoBehaviour they inherit from a generic class like so:

public abstract class MonoBase<T> : MonoBehaviour where T : MonoBase<T> { }

public class Example : MonoBase<Example> { }

Now, I would like to make an editor script for all those MonoBase classes, and the closest I have gotten to a solution that will compile is :

[CustomEditor(typeof(MonoBase<>))]
public class MonoBaseEditor<T> : Editor where T : MonoBase<T>
{
    public override void OnInspectorGUI() {
        this.DrawDefaultInspector();

        MonoBase<T> myScript = (MonoBase<T>)this.target;
        if (GUILayout.Button("Build Object")) {
        }
    }
}

But of course it doesn't really work.

Am I missing something or is there really no way to make a generic editor class like it's mentioned in an answer here.

\$\endgroup\$
2
  • \$\begingroup\$ Quoting your link, "The Editor class is derived from ScriptableObject and as such it can't be a generic type. You need a concrete class and the class name has to match the filename. You could have a generic base class like this but the actual editors must not contain a generic parameter." That looks pretty definitive to me. It matches the tests I've done and it sounds like you're observing the same results. Is there a reason to believe this is not the answer? \$\endgroup\$
    – DMGregory
    Jan 31, 2020 at 13:50
  • \$\begingroup\$ Well the answer is from 4 years ago, and I though about posting here in case someone has a different solution to my problem or if it became possible in some way in the meantime :D That answer states that you can't make a generic editor class (which I don't really need), I just need an editor script that will work with my generic class. \$\endgroup\$ Jan 31, 2020 at 13:56

3 Answers 3

5
\$\begingroup\$

Well, you can make an Editor class for it, but Unity can't instantiate a generic one, when you instantiate a generic class, you must specify the generic type, as example, when you try do do this C# will throw an error: List<> l = new List<>();. And there is no way for Unity to safely assume or guess what T should be.


You can get it to work by creating a custom editor for each T, so that Unity can instantiate the custom editor where T is known, see the other answer of Ed Marthy on how to do this


We can also make a regular Editor, we just can't cast target to MonoBase<T> because we don't know T, And if you did know for sure that T is Example in this case, you should just write a custom editor for Example instead of MonoBase<> in the first place...

The closest thing you can cast 'target' to is the (Non-generic) type that MonoBase<> inherrits (MonoBehaviour in your example).

[CustomEditor(typeof(MonoBase<>), true)] // <-- the 'true' is important, it makes sure that classes that inherrit MonoBase use this editor aswell
public class ExampleEditor : Editor
{
    public override void OnInspectorGUI()
    {

        if (GUILayout.Button("Build Object"))
        {
            MonoBehaviour possible = (MonoBehaviour)target;
            Debug.Log(possible.name);
        }
    }
}

In your case, because the T in MonoBase<T> has to be a MonoBase<T> itself. You really can't cast to anything MonoBase. If the where clause of T would be something like where T : MonoBehaviour instead of where T : MonoBase<T>, and you would be willing to use an interface rather then abstract class, then you could make it so you can cast target to IMonoBase<MonoBehaviour>. Using the answer to this question.

public interface IMonoBase<out T> where T : MonoBehaviour
{
    IMonoBase<T> ExampleMethod();
}

public abstract class MonoBase<T> : MonoBehaviour, IMonoBase<T> where T : MonoBehaviour
{
    public abstract IMonoBase<T> ExampleMethod();
}

public class Example : MonoBase<Example>
{
    public override IMonoBase<Example> ExampleMethod()
    {
        return this;
    }
}

Editor script:

[CustomEditor(typeof(MonoBase<>), true)]
public class ExampleEditor : Editor
{
    public override void OnInspectorGUI()
    {
        if (GUILayout.Button("Build Object"))
        {
            IMonoBase<MonoBehaviour> test = (IMonoBase<MonoBehaviour>)target;
            IMonoBase<MonoBehaviour> example = test.ExampleMethod();
            Debug.Log(example is IMonoBase<Example>);
        }
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ I realized I could of just said T was MonoBehaviour instead of MonoBase<T> but was so interested to find out other ways of making this work. Both solutions are great, thanks <3 \$\endgroup\$ Jan 31, 2020 at 15:03
6
\$\begingroup\$

Another option I’ve used is to have separate editor classes for each class, but they all extend from the base editor class and do nothing themselves.

public class MonoBaseEditor<T> : Editor where T : MonoBase<T>
{
    public override void OnInspectorGUI() {
        this.DrawDefaultInspector();

        MonoBase<T> myScript = (MonoBase<T>)this.target;
        if (GUILayout.Button("Build Object")) {
        }
    }
}

[CustomEditor(typeof(Example))]
public class ExampleEditor : MonoBaseEditor<Example>
{}

[CustomEditor(typeof(AnotherClass))]
public class AnotherClassEditor : MonoBaseEditor<AnotherClass>
{}

[CustomEditor(typeof(ThirdClass))]
public class ThirdClassEditor : MonoBaseEditor<ThirdClass>
{}
\$\endgroup\$
1
\$\begingroup\$

This actually works more simply now with more recent versions of Unity. You don't need to worry about the filename matching and you just need an interface for OnInspectorGUI()

e.g.

// ScriptableIndex<T> : General purpose ScriptableObject automatic indexer
public class ScriptableIndex<T> : ScriptableObject, IScriptableIndex where T : ScriptableObject

public interface IScriptableIndex
{
    public void OnInspectorGUI();
}

[CustomEditor(typeof(ScriptableIndex<>), true)]
public class EditorScriptableIndex : Editor
{
    public override void OnInspectorGUI()
    {
        IScriptableIndex editObject = target as IScriptableIndex;
        if ( editObject != null )
            editObject.OnInspectorGUI();
    }
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .