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.

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();
}

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();
        }
    }
}