Search Unity

AddComponent(string): API Removal in Unity 5.0

January 21, 2015 in Technology | 6 min. read
Log when game enter in play mode
Log when game enter in play mode
Share

Is this article helpful for you?

Thank you for your feedback!

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 &nbsp;/ 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

January 21, 2015 in Technology | 6 min. read

Is this article helpful for you?

Thank you for your feedback!