Assign a variable of type "type" on the inspector or best work around

Hello.
Can I have a variable of type “Type” in a scriptable object, and can I assign it somehow in the inspector?

What I want to achieve

Code in the scriptable object:

public class BaseSkill : ScriptableObject
{
    public Type skillImplementationType;
}

Code where I use that info:

gameObject.AddComponent< myBaseSkill.skillImplementationType >();

Why

I am making an RPG and skills are monobehaviours attached to character gameobjects. Since many skills have similar behaviours but different attributes (for example, a FireBall deals 40 damage and GreatFireBall 60) I do not need a different monobehaviour for every skill. So I have a BaseSkill scriptableobject with all the info about a skill. I want to somehow store in that scriptableobject what class do I need to add to a gameobject so the character it represents can use said skill.

So, both “fireball” and “greatfireball” would add the class “projectileSkill” to the gameobject, but the “GroundSlam” skill would add the “areaDamageSkill” instead.

  • All skills inherit from a Skill
    class.
  • There are no other instances
    of that monobehaviour present so
    neither “typeof()” nor “GetType()”
    are of any use.

Is there any way to implement this?

The best workaround is to implement a custom editor or propertydrawer that allows you to select a type and have a wrapper to store the type as assembly-qualified type name. I’ve written such an editor extension some time ago but can’t find it ^^. However i’ve found this solution that numberkruncher posted.

Though everything MacDx said is right so make sure you use the System.Type version of AddComponent and not the generic version.

edit

I just found my general purpose “SerializableType” class. It allows you to serialize any System.Type that represents an actual type, even types with generic arguments. Of course generic types can’t be used in AddComponent as Components need to be concrete types without generic parameters. It’s only a serialized version of System.Type. It doesn’t include any “visual” editor magic. Along with the SerializableType i’ve also created a SerializableMethodInfo. Though if you’re not familiar with reflection you may just ignore this ^^.

Apart from the general purpose SerializableType i’ve also found my MonoScriptAttribute and the associated property drawer (MonoScriptPropertyDrawer). It allows you to simply attach the [MonoScript] attribute to a serialized string variable and get an object field where you can drag and drop monoscripts from the project (script assets). It will store the type name in the string variable. The MonoScript attribute also allows you to filter the allowed types.

[MonoScript(typeof(IMyInterface))]
public string componentTypeName;

This would only allow dragging component types to the object field which implement the given interface. It was just a simple implementation. It may be useful depending on the exact usecase.

second edit

I recently made an actual wrapper class (SerializableMonoScript) for a MonoScript reference that can directly be used in a runtime class. It also has a property drawer that allows drag and drop. Since Unity now more or less supports generic types, instead of an attribute to restrict the types there’s now a generic version that allows you to specify the base type.

public SerializableMonoScript scriptTypeReference;

The class has a property called “type” which directly gives you the System.Type of the assigned MonoScript. Internally it still just stores the assembly qualified type name as a string.

Not directly related to this question, but I also made a “SerializableInterface” which is just a wrapper for a UnityEngine.Object reference but allows to filter for a certain interface. The propertydrawer will assign only instances (MonoBehaviours or ScriptableObjects) which implement that interface. It also has a cached direct access to the casted interface for convenience.

What you can do is parse a string into a Type using the static GetType method from the Type class. Something like:

//This will add a Fireball script to the gameobject
string typeString = "Fireball";
Type t = Type.GetType(typeString);
gameObject.AddComponent(t); //Assuming Fireball inherits at least from component, of course.

Hope this helps!

By the way AFAIK, this is not correct C# :

gameObject.AddComponent< myBaseSkill.skillImplementationType >();

You cannot use an expression as the generic parameter for the method or class, it should always be a class name. Unity provides methods with normal Type parameters for those cases (Where said type inherits from unity’s object, or component in this particular case).