[solved] Load components via textfile config / AddComponent(string) with Unity5

I just upgraded from Unity 4.6.2f1 to Unity 5.0.0f4.

In my project I use a config file with names of components that should be loaded at runtime, via the gameObject.AddComponent(string) method.

Since Unity 5 this method is deprecated and the auto updater replaced it with a call to UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(...) which it tells me is not safe to use in the release build and I should use the generic Version of gameObject.AddComponent() or gameObject.AddComponent(Type) to add my components.

That’s no option for me, as I want to load the components at runtime and I only know the name (string) of that component from the configuration file and not the specific type, but only that of the base class.

Anybody any idea how to fix that?

What I do is this: I have a base class, and inheret different subclasses to achieve different behaviours for different configurations, so you’re able to switch behaviours by modifying the config file, without recompiling/building the whole project.

This is an example of how implemented a kind of strategy pattern, where the subclasses override abstract and virtual functions of the base class.

config file snippet

<config 
  input_listener = "TestInput01"
/>

input listener base class

using UnityEngine;
using System.Collections;

/* \class InputListener
 * 
 * Base class for all input listeners
 */
public abstract class InputListener : MonoBehaviour 
{
    public abstract ParseInput();
}

input listener 01

using UnityEngine;
using System;
using System.Collections;

/* \class TestInput01
 * 
 * A simple input listener
 */
public class TestInput01 : InputListener 
{
	/* ParseInput() 
	 * 
	 * Evaluate keys pressed
	 */
	public override void ParseInput() 
	{
		// do something when the escape key is pressed
		if (Input.GetKeyDown (KeyCode.Escape)) 
		{
            // do something ...
		}
	}
}

input listener 02

using UnityEngine;
using System;
using System.Collections;

/* \class TestInput02
 * 
 * Another simple input listener that does something else
 */
public class TestInput02: InputListener 
{
	/* ParseInput() 
	 * 
	 * Evaluate keys pressed
	 */
	public override void ParseInput() 
	{
        // do something else when the escape key is pressed
		if (Input.GetKeyDown (KeyCode.Escape)) 
		{
			// do something else
		}
	}
}

game controller

/* \class GameController
 */
public class GameController : MonoBehaviour 
{
	public const string APPLICATION_PATH = Application.dataPath;
	public const string RESOURCE_PATH = "/Resources/";
	public const string CONFIG_PATH = "Config/";
	public const string CONFIG_NAME = APPLICATION_PATH + RESOURCE_PATH + CONFIG_PATH + "configs.xml";

	// configuration files
	private XmlConfigs configList;
	private string configName;;

    // yadda yadda ...

 	/* InitializeConfig()
	 * 
     * Load components according to the specified config name in the configuration file
	 * 
     * call the subclass input listeners method via GetCompenent<InputListener>().ParseInput()
	 */
	private void InitializeConfig()
	{
		// load and deserialize XML config file
		configList = XmlSupport.DeserializeXml<XmlConfigs>(GameController.CONFIG_NAME);
		configName = configList.active;
		
		// find the specified config
		foreach (XmlConfig xmlConfig in configList.configs) 
		{
			// and add the component
			if (xmlConfig.name == configName)
			{
                // that's how it worked for me in Unity 4.6.2
                gameObject.AddComponent(xmlConfig.inputListener);

                // that's what the autoupdater made of it, and it won't build
		        // UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(gameObject, "Assets/Resources/Scripts/Controller/GameController.cs (452,5)", xmlConfig.dropBehaviour);
				break;
			}
		}
	}

}

You can always look them up yourself. (something like this- uncompiled, may have typos)

InputListener AddMyInputListenerComponent(string componentTypeName)
{
   if(componentTypeName=="TestInput01")
      return gameObject.AddComponent<TestInput01>();
   if(componentTypeName=="TestInput02")
      return gameObject.AddComponent<TestInput02>();   
}

You could make this function even more general, by returning a MonoBehavior, rather than an InputListener.