Search Unity

GameObject::AddComponent(string) has been removed in Unity 5.0.  The alternatives are two overloads of this API, one taking the component type as a generic type argument and another one taking a reference to the component type. Automatic API updating has been configured but not all cases can be handled automatically.

In our quest to improve Unity we realized that we should let GameObject::AddComponent(string) go; in our first attempt we were too “aggressive” and removed GetComponent(string) as well. However, after receiving some great feedback, we reevaluated this decision and reintroduced it.

Unfortunately we could not do the same with AddComponent(string) (it would reintroduce the very same dependencies we are aiming to break, i.e, from core to subsystems). If your project uses this method you have two possible alternatives (depending on the accessibility of the type as explained in the rules related to compilation order and how the type name is specified). We’ll further describe these options in the following paragraphs (examples are written in C# but the same concepts applies to UnityScript (js) / Boo).

Scenario 1: Name of the type being added is passed as a constant / string literal and the type is known at build time

This scenario is straightforward and easily identifiable: AddComponent() is called with a constant string and you are able to declare local variables (or parameters, or fields) of the type being added. In this case you can simply use the generic version of AddComponent(), as shown in the following example:

[csharp]
// each class declared in its own file
namespace N1
{
class C1 : MonoBehaviour { }
}

class C2 : MonoBehaviour { }

class MyObj : MonoBehaviour
{
public void Start()
{
AddComponent(“C1”); // only the class name, no namespace information….
AddComponent(“C2”);
}
}
[/csharp]

should be changed to:

[csharp]
// C1 / C2 declared as before.

class MyObj : MonoBehaviour
{
public void Start()
{
AddComponent< N1.C1>();
AddComponent< C2>();
}
}
[/csharp]

when porting the project to Unity 5.0.

Note that classes declared inside namespaces (C1) will require the namespace to be specified (line 8). This is unlike the version of this API that used to take a string, it only expects the class name).

Scenario 2: Parameter  / field / local variables are passed as the argument to AddComponent.

This scenario is also easily identifiable by simply checking the call to AddComponent() (it will use a field / local variable / parameter as the argument). Unfortunately, in this case the alternative is to rely on Type.GetType(string) to retrieve a reference to the type at runtime and call AddComponent(type) – which comes with some drawbacks:

  1. Scenarios involving  AOT / stripping
    The same constraints that apply to platforms apply here. For instance, the AOT compiler may not include a type in the final binary if no reference to that type exists in the code (i.e, only the type name, as a string, is used). Also, keep in mind that on IL2CPP backend, stripping is always enabled.

  2. Requires the type’s assembly fully qualified name
    This is more an annoyance than an issue, nevertheless, assembly qualified names can be pretty big, intimidating and prone to syntax errors.

  3. Requires an actual .Net type to exist
    Some Unity components only existed in native code (i.e, no .Net counterpart) (for instance, EllipsoidParticleEmitter). We added .Net representation for such components (from UnityEngine) but we may always have overlooked one or more types ;)

  4. Performance overhead involved in type lookup and / or other tasks.
    Usually this should not be an issue unless Type.GetType(string) gets called in a tight loop, nevertheless, you should keep it in mind.

Automatic API Updating

Since we want to make project updates as easy as possible, we configured our API Updater to take this change into account. When calls to AddComponent(string) are found, the updater analyzes the value used as the argument and, if it succeeds in resolving the type (which excludes the cases described by scenario 2 as the type name is only known at runtime) it simply applies the mechanics described in scenario 1 replacing the call to AddComponent(string) with a call to:

[csharp]AddComponent< T>() // for C#[/csharp]

 

[js]AddComponent.< T>() // for UnityScript (js) [/js]

and AddComponent[of T]() for boo.

(“T” being the type of the component being added)

While planning how to handle the scenario in which the updater is unable to resolve the type being passed as the argument (scenario 2 and also some sub cases of scenario 1 – classes declared in namespaces for instance), we’ve tried to achieve a balance between usefulness, reliability and ease of use. After our first evaluation we came up with the following options:

  1.  Do not touch the script at all (not good);
  2. Simply replace all invocations to AddComponent(string) with its generic version (or the one taking System.Type as its parameter) and assume / hope it works;

  3. Replace such invocations with more elaborate, possibly slow, code that tries to resolve the type at runtime looking in multiple assemblies.

Option 1 got discarded really quick and option 2 did not look very user friendly / reliable or professional to us, so we’re left with option 3 which, to be fair, didn’t sound like a great solution either. In the end we settled on what we consider to be a good compromise:  calls to AddComponent(string) that fall into this category (type cannot be resolved at compile time because it is passed through non constants / non literals) are replaced by a call to APIUpdaterRuntimeServices::AddComponent(…) which, in the editor, is implemented as described in option 3 with extra logging to provide developers hints on how to replace the call with something that is production quality. Below you can see a screenshot of such a log:

Log when game enter in play mode

Log when game enter in play mode

(note that the line / column displayed in the log may be slightly off due to other updates being applied; also it is worth mentioning that the explanation uses C# generic method syntax – so if your scripts are written in UnityScript/Boo make the appropriate changes).

In the above example, even though the updater was unable to resolve the type, when in play mode, i.e, when using the editor version of UnityEngine assembly, APIUpdaterRuntimeServices::AddComponent() found out that the component being added can in fact be resolved and suggested replacing that call with a call to the generic overload of GameObject::AddComponent().

Due to platform constraints (this method uses methods / types not supported on all platforms) and performance implications for players, this method is marked as obsolete and you’ll get an error if you try to build the game, meaning that you do need to take some action to make your code ready for production.

While we understand that this is not a perfect solution we do believe that this is a necessary step to untangle this dependency, and we feel that the Unity 5.0 launch is the perfect time to make this change.

Was this information useful? Do you have any compliments? comments? complaints? Drop me a message!

@adrianoverona

27 Comments

Subscribe to comments

Comments are closed.

  1. I have an error when upgrade to unity 5 and use ios sdk pro..
    Assets/U3DXT/Examples/social/SocialTest/SocialTest.cs(17,9): error CS0118: Twitter' is a namespace’ but a `type’ was expected..
    plz how to fix this

    1. Adriano Verona

      March 20, 2015 at 3:34 pm

      Hey, please, ask in the forums http://forum.unity3d.com/. If you don’t get an answer, report a bug with a repro case,

  2. So my use case, it sounds like there’s a solution using Type.GetType but I thought I’d run it by you incase there’s a better solution. I have 2 DLLs, an editor DLL and a um, … runtime DLL. The point of me mentioning they’re DLLs (plugins) so to point out conditional compilation won’t work as they’re already compiled. The runtime DLL want’s to ‘AddComponent’ a component that exists in the editor DLL if it exists. In other words, if you’re in the editor the runtime dll will add this component, if not it won’t.

    What worked before. AddComponent("MyEditorHelper") would fail if not in the editor. I don’t care if it fails. No problem. Now though I can’t do it because AddComponent<MyEditorHelper> requires the editor .DLL to compile and the editor DLL requires UnityEditor so that’s a fail.

    Is there some other way I should do this or is Type.GetType the right solution for this?

    1. Adriano Verona

      March 16, 2015 at 6:23 pm

      Based on your description I’d say that Type.GetType(string) is the right option; you’ll need to specify the assembly name though (see https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx)

  3. I’ve never been able to use System.Type.GetType(string) with types defined in the UnityEngine namespace. I’m pretty sure they’re shielded from reflection in a way that prohibits this. In my case, the type is NOT known at compile time so I cannot use the generic version (I cannot use AddComponent(), for example).

    For example, trying to use AddComponent(System.Type.GetType(“Rigidbody”)) or AddComponent(System.Type.GetType(“UnityEngine.Rigidbody”)) both give the error “AddComponent asking for invalid type”. Is there a different assembly name for these?

  4. Material mat = newBlock.AddComponent(); Not working!!!!
    Error:The type ‘UnityEngine.Material’ cannot be used as type parameter ‘T’ in the generic type or method ‘UnityEngine.GameObject.AddComponent()’. There is no implicit reference conversion from ‘UnityEngine.Material’ to ‘UnityEngine.Component’. Block.cs 25 32 Assembly-CSharp-vs

    1. A Material is not a Component, and can’t be attached as one. I’m not sure what you’re trying to do here.

  5. Nice removal…kudos…

    btw May I know is there any event you are planning to add which will call every time object gets “Enable /disable”.

    I know there is OnEnable() but it wont work for specific things first time and later we need to do some tricks work with start().

    It will be good feature if you add some event which will work exactly like OnEnable() but invokes after Start()….

  6. I have always used type not string I wasn’t even aware a string version existes

  7. Kudos for making that change and going more towards clean code and using generics. I wouldn’t have minded GetComponent(string) to disappear as well but as long as I don’t have to use it (due to lack of alternatives – which we luckily do have), I don’t mind ;-)

    It’s also pretty awesome that you’ve created the API-updater.

    Now, just add Instantiate(Object, Vector3, Quaternion) and I’ll be all happy for now (I’ve seen there’s Instantiate(Object) already – but I really like doing this whole thing in a single statement ;-) )

  8. Really hope more gets removed, maybe then people will stop making horrible “games” like “le fantabuloso” and those dreadful *insert trendy game here* clones. Probably not but I can wish.

  9. I think it’s a good decision. Doing everything via Strings where other argument types are really more appropriate (in this case, System.Type) is not a very good design and quite error prone imho. A string is just a string in the end – if you refactor a type name, your string literal might not be updated (depends on IDE and settings), introducing an unnecessary risk. I can also understand that some people who got used to it are upset about this, but it’s really not a good way to handle things. Instead of using a String literal, it should always be possible to do a lookup in the Assembly with the fully qualified name at runtime, get the Type by name, and feed it into the argument of the other overload of “AddComponent(…)”.

    Since you are cleaning up the API, and since we finally got our hands on the new Unity GUI, how about a redesign of the input manager? It’s a real pain if you want to allow the player to re-bind keys in-game (a standard feature of modern PC games), the configuration window in the editor feels hacky (lots of “magic values” in string properties) and the API for this window is not public, which prevents us from at least writing better input configuration editor windows.

    1. The Input Manager is being completely overhauled – it won’t be ready for 5.0 but should be in a 5.X release. (Believe me, having implemented in-game key rebinding for a Unity title, I feel your pain on that one…)

      FWIW, it’s not that the editor API for the Input Manager is not public, it’s that there is no editor API for the Input Manager. The Inspector for the Input Manager makes changes via the SerializedObject/SerializedProperty system, which is available to you as well.

      1. @Richard Fine: Thanks for the reply. Doing in-game key rebinding for a Unity game really is a pain at the moment.

        I was not aware that there in fact is no API to the input manager (I just thought that it was non-public). I’ve read an article here on the Unity serialization system, but it never occurred to me that I could use it for this purpose.

  10. we use this in a scenario 2 situation, to dynamically construct gameobjects at runtime based on data loaded from JSON or pulled from a server. Now if we upgrade to 5.0 our data will need to contain fully qualified names, and we’ll probably have to set up garbage instances of all the components we use to un-stupid AOT. Not feeling super positive about this change. I feel like there is a legit use case for this, and it’s what we’re doing….

    1. Why not just build a Dictionary in your client code, mapping the names you currently use for your components to their concrete types, and then run your incoming JSON through it?

      You get more flexibility and control than the runtime could previously give you – for example you can skip the fully-qualified-names requirement, or you could deliberately map an old name to a new implementation of a component – and it should be even better for AOT because you have concrete uses of the types in code instead of unpredictable strings (or at least, things should be no worse).

  11. I love seeing cleanup like this happen and Unity 5 is the perfect time to do it.

  12. Very useful information. At east we’ve also been warned that GetComponent(string) *might* also be going away soon.
    Oh and great work on IOS 64 bit releases on the pre order BETA (19,20). Lot’s of crashes! but it at least works. Looks like Unity 5 isn’t only the most feature packed, but also the most challenging release yet for the team.

  13. it makes me sad( I use this one only in one case (when i need to asign component generated by code in editor), but i don’t see any alternatives((

    1. @Nickolai, I assume you are not generating this code dynamically (i.e, at game runtime) and that you know the full type name (since you are generating the code). In this case you can still use AddComponent(System.Type). (unless I am missing something, which can be very well the case ;))

      Can you give more details?

      1. Nickolai Savelev

        January 22, 2015 at 9:59 am

        I Generate script with Wizard, then i have to add it to component and assign params without wizard restart. Now I just remember generated class name and use AddComponent(generatedClassName);

        1. Look into System.Type.GetType(“string_name”); and go from there.

          I generate scripts myself and was using AddComponent(string). Will now simply have to get the type to use with AddComponent() variation. No biggy.

        2. Ok, so I assume you are running your code in the Editor.

          That should not be to different from doing something like:

          AddComponent(Type.GetType(scriptName + “, ” + assemblyName));

          (the actual code should check the return of GetType() to make sure the type was found)

          The only piece of information missing is the name of the assembly.. and that depends on which language the scripts are written and where you save the scripts.

          Easiest way to find that out is to look in Assets\Standard Assets and open the assemblies with ILSpy (or some other tool like that) and look for your “generated” types . Once you find that, the name of the assembly is simply the name of the DLL without the “.dll” extension.

          Another possible solution is to look in all loaded assemblies (in the current domain) searching for your newly generated type.

  14. Linus Ahlemeyer

    January 21, 2015 at 3:20 pm

    Thanks for not removing GetComponent(string). It still has its uses.

  15. +1, I would have removec GetComponent(string) as well, but I can imagine how the feedback was

  16. Less api trash is better. its easier and less confusing for new users, focuses attention on more modern methods and makes people get adjusted to better practices. goodbye API trash. good riddance.

  17. +1 for getting rid of all API ugliness