MonoBehaviour from DLL in AssetBundle Reflection problem

Hello.

I made a project and decided to move its scripts to a .DLL so I can load them later from an AssetBundle. In the project I have the .dll under the plugins folder and I can drag/attach them to GameObjects just fine.

I export the scene into an AssetBundle, in a different project I load the dll with the script files using

m_Assembly = System.Reflection.Assembly.LoadFile(Application.dataPath + "/../" + worldFileName + ".dll"); .

According to log the scripts seems to be loaded and found alright:

System.Type behaviour = m_Assembly.GetType("BirdBehaviour");
 if (behaviour != null)
 {
 UnityEngine.Debug.Log(behaviour.Name); 
 
 }

This returns “BirdBehaviour” in the console.

Then I load my Scene via AssetBundle, the scene is loaded fine but none of the script seems to be working.
The class defined in script file named ‘BirdBehaviour’ does not match the file name!

I could make a script to use
m_Assembly.GetType(“BirdBehaviour”);

and then AddComponent but that will make things complicated and won’t allow me to edit the public variables of scripts in the editor of the Scene I am exporting as a bundle. Is there a solution to have the referenced scripts properly load from the dll I have loaded?

[edit(2012-09-28)]

Ok, i finally found the problem. It is possible to have MonoBehaviours in an external DLL and load it dynamically at runtime. The problem was the namespace. Usually when you create a dll it uses it’s own namespace. When you import such a dll into a Unity project the namespace seems to disappear and Unity just grabs all MonoBehaviour classes it can find in the dll.

When you load the dll at runtime Unity has problems to “find” the classes inside the assembly. You can get the System.Type object of your class, but AddComponent will complain it can’t find the class.

The solution is: Just remove the namespace when you build your DLL. put all classes into the global namespace and it will work :wink:

After you’ve loaded the assembly you have to retrieve the System.Type object for your class and use it in AddComponent.

Note: AddComponent with a string parameter won’t work unless you added the component at least one time with the Type-object. After that the MonoBehaviour is known to the system and the string version works as well, but not for the first time.

Since it is possible to directly load MonoBehaviour derived classes from an assembly my old answer becomes almost useless :wink:

[/edit(2012-09-28)]

You can’t have MonoBehaviours in an external dll. MonoBehaviours have to be registered in the AssetDatabase.

It’s fine to load “normal” classes that way. On this basis i developed my own “plugin” interface. Something like that:

public class Plugin
{
    public MonoPlugin container;
    public virtual void Awake() {};
    public virtual void Start() {};
    public virtual void Update() {};
    //[...]
}

This interface should be in a common dll which is used by your plugin dll and your actual project. Now just create a MonoBehaviour in your project that implements all unity function your plugin interface needs and call the interface functions from your plugin object:

public class MonoPlugin : MonoBehaviour
{
    public Plugin plugin;
    void Start()
    {
        plugin.Start();
    }
    void Update()
    {
        plugin.Update();
    }
    //[...]
}

Now you can create an extension method for GameObject and / or Component which allows you to add a MonoPlugin:

public static class MonoPluginExtension
{
    public static Plugin AddPlugin(this GameObject aObj, System.Type aPlugin)
    {
        MonoPlugin MP = aObj.AddComponent<MonoPlugin>();
        Plugin P = (Plugin)Activator.CreateInstance(aPlugin);
        MP.plugin = P;
        P.container = MP;
        // Awake can't be wrapped since it's called within AddComponent
        P.Awake();
        return P;
    }
}

Finally you can derive your plugins from Plugin and put them in an external dll. Hopefully Unity will support using MonoBehaviours directly the other day.

ps: I’ve written that just from scratch. So no syntax checks. It’s just a prove-of-concept :wink:

I just now did what you tried to do. I have created an assetbundle for the whole scene. All the gameobjects in that scene will not have any scripts attached. Then I built a .DLL for all the scripts which are going to operate on the gameobjects for the scene.
Now in a new project, I have loaded the scene assetbundle and loaded the scene additively. Then using AngryAnt’s technique, I have downloaded the .DLL assetbundle as text asset and loaded the assembly using reflection. Then to do the work of attaching the scripts to the gameobjects in the loaded scene, I called a predefined method of a predefined class from the same assemby which knows which scripts are needed to be attached to which gameobjects and what public data members of the scripts are needed to be set. It worked perfectly for me.

Note that new project doesn’t know anything about the models in the assetbundle and doesn’t know anything about any scripts in the dll either.

If you need a working example, revert me back.

If you have got a better solution please share it.

Here is the working sample. Tested for Win32, Android and WebPlayer (on Unity 3.5.5f2 Pro)
It needs mono to generate the assembly.
Mono can be downloaded from here.

Samples can be downloaded from [Sorry! Please write me, I will surely send the files].

NOTE:

  • Assembly can be created by running
    “DynamicScript\Remote\Assets\Scripts\makedll.bat”.
  • Assumed that mcs is added to PATH environment variable.
  • Update the path in “DynamicScript\Host\Assets\Scripts\SceneLoader.cs”
    before compiling.
  • Build AssetBundles from Remote app from Assets Menu of unity editor