how to use dynamic parameter for AddComponent

i want to dynamicly add script to some gameobject.
in the main script i have a Object array which holds all the scripts i will use

public Object[] allScripts;

in some condition say 600 frame of the game start.
i want to addcomponent to my gameobject like

gameObject.AddComponent< allScripts[0] >();

this is not working.some how i need this and i need the generic parameter to be variable.
is there a way to do this?

The generic parameter has to be a compile-time Type. This means that you cannot put a variable in there. This is a hard restriction on C#, so no getting around it.

For Unity, you can use the non-generic version of AddComponent, which takes a type variable as a parameter. In your case, you’d use it like this:

gameObject.AddComponent(allScripts[0].GetType());
1 Like

Okay, there’s a work-around where you use another method’s parameter to do this, but I find it somewhat awkward. Note that the AddOneScript method needs to take a parameter that it never uses:

public Component[] myScripts;

void AddAllScripts()
{
    foreach (var item in myScripts)
    {
        AddOneScript(item);
    }
}


void AddOneScript<T>(T scriptType) where T : Component
{
    gameObject.AddComponent<T>();
}

I’m using Component since AddComponent needs the variables it takes to be a Component, not a UnityEngine.Object (or System.Object). I haven’t tested it, and it’s kinda messy, but it’ll probably work, if you really feel like you want to do that.

3 Likes

thank you Baste,get a little problem,that if i change Object to Component,i can’t drag script to Component slot in Editor,Object type can.what shall i do?
it seems the script type is MonoScript,not Component.
when using AddComponent if i direct write the script name,i dont’ know why they just turn to be Component type automatically.

Could you change Component to MonoBehaviour?

i don’t konw how to convert that.

gameObject.AddComponent< ((Component)(allScripts[0])) >();
gameObject.AddComponent< allScripts[0] as Component >();

seems all fail

error CS0039: Cannot convert type UnityEditor.MonoScript' to UnityEngine.Component’ via a built-in conversion

MonoScript is a weird beast. First of all, it only exists at editor time, which makes sense, as the scripts themselves are not included in a build (only the compiled code is). This means that drag-and-dropping a script component into a list, and then adding scripts from that list to object won’t work in runtime.

If you’re doing this stuff editor time, you could use a list of MonoScripts, and do the same thing as I posted, just substituting this call:

AddOneScript(item);

with this:

AddOneScript(item.GetClass());

Assuming that item is a MonoScript. GetClass() gives you the type of the script the MonoScript defines.

still get error

The type System.Type' must be convertible to UnityEngine.Component’ in order to use it as parameter T' in the generic type or method MainScript.AddOneScript(T)’

public MonoScript[] allScripts;

void Start()
{
    foreach (var item in allScripts)
    {
        AddOneScript(item.GetClass());
    }
}

void AddOneScript<T>(T scriptType) where T : Component
{
    gameObject.AddComponent<T>();
}

does it mean there is no way to convert MonoScript to Component type

tried chage array to list

public List<MonoScript> allScripts;

same error.

ok,maybe i should ask is there a way to compile script to Component at run time?
i don’t know c# too much,but i heard c# can compile script at run time.
say there is a class script saved as string,and this string can be compiled to be instance.
is that true?if it is.can i use this to compile it to Component?But it seems i still don’t know how to convert it to be Component type

MonoScript is not a Component.

This means that not all MonoScripts you add are able to be used with AddComponent. Some may be, some may not, compiler does not know that. That is why the “where T : Component” is introduced. This tells the compiler that you are only going to give Component types to that function.

You could try (I have not tested this, since i am at work and do not have Unity here)

void Start() {
  foreach (var item in allScripts) {
    if (item is Component) {
      AddOneScript(item.GetType());  // may have to cast item to Component using "Component(item).GetType()"
    }
  }
}

That’s not how you do a cast, dterbeest.

You’ll want to cast it this way:

AddOneScript((Component) item.[URL='http://unity3d.com/support/documentation/ScriptReference/30_search.html?q=GetClass']GetClass[/URL]());

Note again that this won’t work in a build, as the UnityEditor namespace (that contains MonoScript) does not exist there.

Look, what you’re trying to do isn’t really viable. Compiling at runtime is also a bad idea - it’s slow, it’s a lot of work, and it won’t work on a lot of platforms as they don’t allow that stuff (IOS is an example).

You’re probably trying to solve a specific issue - if you post that, we can try to find a better solution than what you’re trying to do.

1 Like

hahaha… i’m so sorry… that is how you do a cast in the wonderfull Pascal language, which i use at work… was a bit mixed up… sorry for that

thank you all for the repley.finally i think i’d better give up coding like this.
change some of my struct and avoid of this.

You can add a script as a component at runtime using reflection if you have a string holding the scriptname.
Would that be suitable?

    public GameObject[] gameObjects;

    private void Start()
    {
        AddComponents<CapsuleCollider>();
    }

    private void AddComponents<T>() where T : Component
    {
        foreach (var targetGameObject in targetGameObjects)
            targetGameObject.AddComponent<T>();
    }
1 Like

Thank you so much for this!! My problem is that my function similar to AddComponents() had been working with various types of components but when I tried to send it a SphereCollider, it would exit without letting me debug it, and create a unity [Debug Updater] object in the hierarchy as a result.
By adding the “where” to the signature, it suddenly started working with SphereCollider too. Thank you!