- Home /
How to resolve script references in scene bundles from dynamically loaded assemblies?
I've read the forum posts and manual regarding the inclusion of precompiled scripts in asset bundles. However, all the examples I seen involve creating/attaching a new component from an asset bundle assembly. Is there a way to detect/resolve script references from a scene that was loaded through an asset bundle with an assembly that was loaded from another bundle (or, loaded directly through a .dll file).
I'm creating a Unity based visualization application. It does little more than allow users to navigate through a virtual scene and provide some basic interaction functionality. I'd like users to be able to generate their own content in the Unity Editor (a separate project) and then export that content (scenes) in a way that the visualization application can ingest and display. AssetBundles seem like the way to do that. I can do that now, and it works fine. The problem is that if an object has a script attached to it (maybe to make it move or perform some behavior), the script reference can't be resolved in the visualization application. I've tried loading an assembly with the precompiled script code, but I need more information on how script references are resolved when scene bundles are deserialized to see if it's possible to resolve them from an alternate assembly.
Please see the following forum entry for more detail. http://forum.unity3d.com/threads/exporting-resolving-script-references-in-assetbundles.331110/
Thank you.
Does this mean one can create asset bundles with a scene in it but that scene can not have it's own scripts? That would be really, really terrible.
I'm having myself the problem at the moment, that I loaded a scene from an asset bundle but none of the scripts in it are referenced.
If my project needs to know (contain) all the scripts that I use in an asset bundle then the entire idea of using asset bundles is pointless.
Antx,
That's been my experience, and I agree. We also load content dynamically through Asset Bundles. As of now, content can't contain custom scripted behaviors.
Script references in AB will resolve if the same script is compiled into the player that is loading the AB. Not sure how fragile that is, as I'm sure there might be differences in IDs in the asset database.
As noted, if the script is part of a plugin, you can load the plugin manually first using the .Net Assembly classes. Then load the AB and references should resolve.
Okay, thanks for the reply. I would need the loading of scripts via asset bundles on android and iPhone. If I got that right, the ".Net Assembly classes" solution would not even work there.
Answer by mikewarren · Jun 22, 2015 at 08:53 PM
Unfortunately, no answers here or on the forum.
The closest I've come to making script references resolve is by using plugins. U5 makes it pretty easy to code a script in an external (VS or Mono) project and load the assembly in the editor as a managed plugin. As such, all scripts contained within the plugin can be instantiated and edited in the editor as if they had been compiled by the editor.
If a scene bundle uses a script within the plugin, all the bundle loader (in a different project) need do is load the plugin (Assembly) dynamically first, and references in the scene bundle seem to resolve.
I need to test more, but this seems the closest way possible towards letting content developers use the Unity editor to export assets and scripts as a complete package.
It's unfortunate that setting up and editing the plugin project is a bit cumbersome for a non-programmer. It would be nice if the editor could be told to compile and export scripts as a managed plugin (rather than putting them in Assembly-CSharp) with a name of my choosing (similar to the way asset bundles are created). I've requested the feature on the feedback forum. Others have suggested the same, though for differing reasons.
If anyone can figure out an editor script to do it, please provide.
Thanks.
Well, you can use EditorUtility.CompileCSharp to compile the given list of files into a custom assembly. However you have to provide the list of referenced assemblies and preprocessor defines yourself. Also keep in $$anonymous$$d that you must not have a class in that assembly that is already in your actual project. Otherwise your assembly can't be loaded.
If your application is highly modular you might want to compile each script into it's own assembly. However that will give you nightmares when it comes to dependencies. Scripts would not be able to reference each other or form ring dependencies since each is compiled seperately.
If you compile all your plugin / extension scripts into one assembly the biggest problem would be to "exclude" your scripts from your "normal" project assembly which you most likely want to add to the references. One solution is to provide an API which is already compiled into a seperate assembly which provides base classes and interfaces so you don't have any dependencies between the project assemblies and your plugin assemblies. All those will only depend on the API assembly.
Since CompileCSharp is an undocumented function, here's it's signature just in case intellisense doesn't work:
public static string[] CompileCSharp(string[] sources, string[] references, string[] defines, string outputFile)
This method just wraps the internal "UnityEditor.Scripting.$$anonymous$$onoCSharpCompiler.Compile" method which has the same signature. The method returns the message output of the compiler.
mikewarren could you guide me on how to do this? I'm kinda frustrated now.
@mikewarren just found this question of yours and it's the exact same problem I'm trying to solve at the moment. Could I ask you some questions?
Have you found a better way to solve this problem in the meantime?
I understand every step in your description except this one:
If a scene bundle uses a script within the plugin, all the bundle loader (in a different project) need do is load the plugin (Assembly) dynamically first, and references in the scene bundle seem to resolve.
I'm not sure how to load the plugin dynamically. What exactly do you mean by that? And does this also mean, that it isn't enough to just have the plugin in the project folder of the the project that loads the asset bundle?
Thank you very much!
Don't post an answer on a Q&A site when you don't answer the question at the top. If you have a question you should ask a seperate question.
To answer your questions:
No, there's no other way. Unity itself does not support exporting "scripts" and loading them at runtime. They need to be compiled into an assembly.
You can load assemblies dynamically through reflection. Just use Assembly.Load and pass in the DLL file content.
Note that it is not possible to "unload" an assembly once loaded. You have to restart the application. The plugin assembly data could be loaded either through normal File io or through WWW
I'll convert your answer into a comment.
@Bunny83 I'm sorry, I didn't mean to post an answer but rather a comment. Honest mistake on my part, sorry!
And thanks for your answer. Just to make sure I got you right, those are the correct steps?:
Create dll and put it in both, project A and project B
Create scene with prefabs using scripts from the dll in project A
Create scene asset bundle from project A
Write code in project B that first makes a call to Assembly.Load and passi in the dll (I already have added in project B) as byte array, then load the scene bundle
Or in other words: It's not enough just having the dll in the project loading the asset bundle, I really also have to explicitly load the asset?
Also, if you don't $$anonymous$$d me asking: Do you know how asset bundles reference scripts from assemblies? What I'm getting at is: What kind of changes (if any) can I make to the dll without having to rebuild the asset bundle referencing it? Thank you very much!
Posted a full answer, but I messed up the whole answer vs comment thing, too and the system rejected it. I think this is a great place to ask for clarification to answers given.
The short answer is that in my experience if you want scripts from an asset bundle in project A to be resolved when loaded into Project B, those scripts must
Be defined in a plugin in Project A (not compiled from C# code by Unity)
That plugin assembly must be loaded into Project B (at runtime) via an Assembly.LoadFrom call $$anonymous$$ to loading the Project A asset bundle.
So, it'll work, but it's such a convoluted, error prone work flow, it's unsuitable for all but relatively experienced users.
I don't think that you can include the assembly as part of the asset bundle build, but that would be a great test. Update us on if it works.
Good luck. Feel free to ask me questions if you like.
@mikewarren Thank you very much for your answer, it was super helpful and I got it to work! Concerning shipping the assembly in the asset bundle: No idea if it would work but maybe one could add it to the bundle (perhaps you can just rename the file ending to a kind of asset that's allowed in a bundle? no idea..) and read it as byte stream. If I ever get around to try this out, and I sure hope so, I'll let you know the results. Thanks again, your help is really appreciated!
Glad you got it working and that I could help.
I think I remember some examples that suggested you could package binary assets by rena$$anonymous$$g them and treating them as text assets, but I don't remember the details.