As far as i'm concerned, the use of asmdef
merely forces unity3d to compile your scripts into separate assemblies that are then referenced by your project.
Actually, it creates projects in your unity solution that contain your .cs
files and each of these projects is compiled into its own output assembly.
The error you are seeing might be related to assembly caching.
I've had that error a few months ago and it was due to an outdated assembly still being cached.
As a result, unity3d editor
kinda hiccuped when loading the project and therefore could not load the specific assembly.
I fixed it by deleting the directories Library
, obj
and Temp
and then reloaded the unity3d project.
To get rid of that for good, we have moved away from asmdef
and .cs
files inside our unity projects once and for all.
All our scripts have been extracted to separate projects that are never touched by unity3d.
Every project references UnityEngine.dll
and/or UnityEditor.dll
(for Editor assemblies) depending on which unity3d types
it may require.
The projects are built locally using Visual Studio
or server side in our CI pipeline
.
Output is copied manually into the assets directory of a unity project where it is then loaded from within unity3d editor
.
This last step is a pain admittedly but i have yet to find time to streamline this process some more.
This approach has a few benefits
- We are in control of our code in one single repository.
There is only one single point of truth and every developer commits changes onto the same code base.
There are no copies of
.cs
files across any number of unity projects that consume our types.
- There is no need to figure out merge conflicts from updating a
unitypackage
where there have been deletions.
- Unit tests can be done server side (
CI pipeline
) without the need of some docker image with unity3d on top (ofc there are restrictions on how much you can test without the entire unity3d environment running).
- We create our own
NuGet
packages that can be referenced in projects (vcproj
, not unity projects!).
- Types deriving from
MonoBehaviour
can be added to GameObjects
via code or via unity3d editor
.
You also get to explore loaded assemblies inside your unity3d editor
project view by clicking on the arrow of an assembly which will expand to show the list of contained relevant types.
Let's talk about downsides
One example is that we use SteamVR for interacting with controls.
The SteamVR plugin for unity3d is released through unity's asset store
and annoyingly it contains script files and resources but no assemblies.
This goes for pretty much all assets in the store by the way.
Since we can't build against code, we have to go through the trouble of compiling SteamVR once and then copy the output assembly somewhere else.
This is not just as tedious as a task can be, it also has some limitations of its own which i get to later.
Anyway, this lets us reference a compiled asset with our own code so we get to use asset specific types like SteamVR_Action
in our code without having to use unity3d editor
and script files in unity projects (or reflection which would be even worse).
Limitations of compiled assets like this are two fold.
For once, it is horribly inefficient to get there in the first place.
On the other hand, you'll only have to do that once for every version of an asset.
Once that's done, make it a private NuGet
package and you're golden.
The other limitation is the way how unity3d approaches dependency injection.
Actually i'm not entirely sure what it really is they try to do but here goes.
Unity3d wants you to only ever reference assemblies from within ../UnityInstallDirectory/Editor/Data/Managed/
.
In a perfect world, your own assemblies reference that big gunky UnityEngine.dll
in this directory and once loaded by unity3d editor
everything works as expected.
When you compile a unity project from within unity3d editor
however, the resulting assembly references all the assemblies from within ../UnityInstallDirectory/Editor/Data/Managed/UnityEngine/
which contains a very small version of UnityEngine.dll
which in turn acts as a type forwarder to all the other sub modules.
Not such a perfect world now is it?
Your previously compiled asset requires the type MonoBehaviour
to sit in an assembly called UnityEngine.CoreModule.dll
.
Your own project however expects it to sit in UnityEngine.dll
since you're a good fellow and follow the rules.
This is just asking for trouble and to get around this problem we are now directly referencing all the managed sub modules from within ../UnityInstallDirectory/Editor/Data/Managed/UnityEngine/
.
We also ignore unity3d editor
moaning about how we are doing it wrong.
tl;dr
By doing all from above and leaving asmdef
and .cs
files out of the equation we are able to build, unit test and pack our logic and types into assemblies.
We are also able to keep a clean code base that can be easily maintained and extended without dozens of copies of the same code in multiple locations and/or repositories.
Why unity3d does things the way they do, i'll never understand.
I do know there is a thing called Building from HEAD but since the entirety of the .net ecosystem is using the binary format to share content in the form of referable assemblies, why would you want to do things differently?
This is a topic for another day though.
If you made it all the way through this post, i sincerely hope it is helping you fix your problem at hand.
In case i misinterpreted your question ... sorry :-)
Unity3d is weird ...
AssemblyBuilder
class. Your answer doesn't show your build process. – MichaelHouseMonoBehaviour
component to be an individual script file with matching name... – derHugo