- Home /
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?
Answer by MacDx · Feb 01, 2018 at 05:29 PM
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).
Thank you very much, that will work fine!
About your side note about the AddComponent function, are you sure? The second description in the manual suggests otherwise. https://docs.unity3d.com/ScriptReference/GameObject.AddComponent.html
No prob. Also, yes I'm sure and the docs do not suggest otherwise. Take a closer look:
This:
gameObject.AddComponent ( myBaseSkill.skillImplementationType);
And this:
gameObject.AddComponent <myBaseSkill.skillImplementationType> ();
Are 2 very different things. And the second one is wrong. Try it out.
If you don't understand the difference between them I suggest you to do some research on C#'s generic methods.
https://docs.microsoft.com/en-us/dotnet/csharp/program$$anonymous$$g-guide/generics/generic-methods
Answer by Bunny83 · Feb 01, 2018 at 09:44 PM
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.
Dude!! This is $$anonymous$$oney!!! The $$anonymous$$onoScriptAttribute. Works perfect.
System.Type type = System.Reflection.Assembly.GetExecutingAssembly ().GetType (componentTypeName); AddComponent(type);
This opens up so many more possiblities to making scripts more modular.
How does this solution ($$anonymous$$onoScriptAttribute and the associated property drawer ($$anonymous$$onoScriptPropertyDrawer)) work when refactoring (rena$$anonymous$$g) an assigned class?
Well, it doesn't ^^. A serialized type name is data, not code. So when you refactor a type name in your code it won't affect the serialized data. Therefore the type that was serialized does no longer exist. In theory It might be possible to not only save the actual type name but also store the GUID of the $$anonymous$$onoScript asset. That way we might be able to detect any refactoring inside Unity. Keep in $$anonymous$$d that changing the name of a $$anonymous$$onoBehaviour or ScriptableObject also requires a change of the actual filename (along with the meta file). So that's in general not a trivial issue.
Keep in $$anonymous$$d that most serialization systems have this issue. For example the BinaryFormatter will completely choke on any serialized data if any of the involved types received a name change or rena$$anonymous$$g / addition / removal of fields.
If anyone would encounter an issue with $$anonymous$$onoScriptAttribute not detecting classes inside namespaces then change script.GetClass().Name
to script.GetClass().FullName;
in line 89.
Your answer
Follow this Question
Related Questions
AddComponent with runtime type checking 0 Answers
GetType is Monoscript, how to implement correctly? store type in variable? 1 Answer
Creating a Script at runtime, and adding it to an object... 1 Answer
Serialize a reference to MonoBehaviour type in ScriptableObject 2 Answers
ScriptableObject problem and creating a new class instance 1 Answer