Inspector variable to hold a type of inherited class

Hey everyone. Im working on a minecraft style engine for Unity. I already wrote one, but im rewriting it to be faster. I have a class called TerrainGenerator, which is obviously for generating terrain. But i want the user to be able to write their own TerrainGenerator. They can make a class that inherits from TerrainGenerator, and use that class for generating the terrain. I’m trying to make this all very simple for the user. So I want to be able to set this in the inspector of my World class.

Heres an example of my base class

public class TerrainGenerator {
    public static TerrainGenerator instance;

    public virtual void GenerateTerrain() {}
}

And a derived class made by the user

public class FlatTerrainGenerator : TerrainGenerator {
    public override void GenerateTerrain () {
        //Generates flat terrain
    }
}

And here’s what I want to do

public class World {
    //somehow set TerrainGenerator to FlatTerrainGenerator in inspector

    void Awake () {
        //if desired TerrainGenerator is set to FlatTerrainGenerator
        TerrainGenerator.instance = new FlatTerrainGenerator ();
    }

    void Start () {
         //generate terrain using desired terrain generator
         TerrainGenerator.instance.GenerateTerrain ();
    }
}

I can easily make TerrainGenerator inherit from Monobehaviour, then stick an instance of FlatTerrainGenerator on a GameObject. Then drag and drop that component into a field in the World inspector. But that’s an ugly solution. I’m looking for a clean and simple way for the user to change the instance of terrainGenerator. Its easy to do in code, but I would like it to be assigned in the editor because of simplicity and the fact that the TerrainGenerator should not be changed during runtime, it only needs to be serialized as a variable in the World component.

I was thinking maybe I could have the Inspector variable a Type. Then the user could set the type to FlatTerrainGenerator. But I don't know if this would work. I can't test that out until tonight because I'm at work

You didn't ask a question in your question, but I suspect you want to know how to create a valid plug-in architecture. If that's the case, here's a great place to start - it's c# in general, not Unity in specific, but it still applies. http://www.codeproject.com/Articles/6334/Plug-ins-in-C

Haha sorry I thought my question was implied when I explained what I'm trying to do. I'll read up on that plugin tutorial when I get a chance and see if I learn something useful!

I just realized I had linked a VB .Net article - sorry about that - I just changed the link to a C# article.

Well there's some useful information there, but it doesn't help me solve my problem. I've edited my question to better explain what I'm trying to do

2 Answers

2

In the class where you have “World” - I’m assuming it’s the script that’s attached to the object?

If so, make a public member of the base class type:

public TerrainGenerator ThisGenerator;

This should show up in the inspector. You’d drag the script for FlatTerrainGenerator to that spot on the inspector.

You don’t have to know the specific implementation since it’s a derived class.

void Start () {
     //generate terrain using desired terrain generator
     this.ThisGenerator.GenerateTerrain();
}

That only works if TerrainGenerator inherits from Monobehaviour and exists as a component on a GameObject. I mentioned that i already know this is possible, and i would prefer not to do this because its an ugly solution

Finally figured it out. Its not exactly how I wanted it, but its still easy for the user. I wrote a small custom inspector which uses reflection to set the TerrainGenerator. You must only type the name of the TerrainGenerator inherited class that you would like to use. Then the custom inspector will check if that class exists, and if it inherits from TerrainGenerator. If it does, it creates an instance of that class and adds it to the World. If the class does not exist, or does not inherit from TerrainGenerator, the string becomes empty, and the TerrainGenerator defaults to itself.

public override void OnInspectorGUI() {
    World world = (World)target;
    DrawDefaultInspector();
    if (GUI.changed) {
        System.Reflection.Assembly assembly = typeof(TerrainGenerator).Assembly;
        System.Type type = assembly.GetType(world.terrainGeneratorName);
        if (type != null && typeof(TerrainGenerator).IsAssignableFrom(type)) {
            world.terrainGenerator = (TerrainGenerator)System.Activator.CreateInstance(type);
        }
        else {
            world.terrainGeneratorName = string.Empty;
            world.terrainGenerator = new TerrainGenerator();
        }
    }
}