We all throw dozens or hundreds of scripts into our Assets folder and let Unity build and load them. Usually this works well, but there are a number of reasons for wanting to compile at least some of your code into a DLL. This post outlines a method for building code with Visual Studio and still being able to debug it with MonoDevelop.
For context, I’m using Windows 7 64-bit, Visual C# 2010 Express, Unity 3.5.6 and MonoDevelop 2.8.2 (shipped with Unity 3.5.6). The information should apply to other versions and possibly even to Mac development, but I haven’t tested it.
The basic approach is:
create a .csproj file that uses wildcards to find all source code
add this C# project to a Visual Studio solution
use an MSBuild post-build target to convert Visual Studio’s PDB symbols to Mono’s MDB format
debug with MonoDevelop from Unity, as normal
source code should -not- be in the Assets folder, but the generated DLL should be
Here’s a handmade .csproj file that demonstrates how this works:
To use this build file, save it as “UnityDLLExample.csproj” in a project folder with the following layout:
-project/
-----UnityDLLExample.csproj (from this post)
-----UnityDLLExample.sln (create with Visual Studio)
-----scripts/
---------SomeScript.cs (put your source code in here, use sub-folders if you like)
-----Assets/ (Unity assets folder)
---------Plugins/ (DLL and MDB files go here)
Notes:
MonoDevelop doesn’t support the Include=“***.cs” wildcard syntax, so you can’t build this project with MonoDevelop
you can build with the MSBuild command line if you don’t want to open Visual Studio
if you have editor scripts mixed in with game scripts, you can find them with Include=“**\Editor***.cs”
if your editor and game scripts are inter-mixed then you need to Include the editor scripts in the editor csproj and Exclude them from the game csproj
Unity comes with two versions of pdb2mdb.exe – only the one in the MonoBleedingEdge folder works for coroutines (that return IEnumerator)
Amazing discovery! I can’t believe this much time has passed and nobody noticed this. (Should be added to Unity docs on working with DLLs.) Now I feel silly for re-rolling it. Oh well. And thanks for taking the time to post your DLL debug solution!
Awesome! I can’t believe the Unity guys never mentioned that thing about the correct pdb2mdb, and that it wasn’t noticed before. Thanks a lot yoyo for this discovery, and guavaman for starting it all
Uhm, I tried using the other pdb2mdb (the one in the MonoBleedingEdge folder) but, while it compiles ok with IEnumerators, it seems it doesn’t work, and Unity’s log doesn’t report error lines (exactly as if you didn’t have an MDB at all).
I had previously tested to make sure I could step into the DLL code and see full source. This works as I described.
I just checked how Debug.Log messages are handled, and what I see is that the Unity console reports the correct file and line in the shared code (so it is using the information in the MDB), but when I double-click on the error message it opens the script that calls into the DLL code, not the DLL code itself.
Note that pdb2mdb.exe generates a new version of the DLL, and that both this updated DLL and the MDB need to be copied into Unity’s assets folder.
Mhmm I use Visual Studio, and use a post-build routine to convert the PDB, and then copy all the files in my project’s Assets folder (DLL, MDB, and eventually XML), but then the Debug.Log messages show no line at all. I wonder, did you try this with Unity 4, which I’m using? Maybe something different happens, even if it would be weird.
I’ve only tried with Unity 3.5.6. Do you still have Unity 3.5.x installed that you could test with?
Also, have a look in your Visual Studio .csproj file and make sure you’ve got DebugSymbols=true and DebugType=full in the configuration you’re building. I’m not sure both settings are strictly necessary, but that’s what I’m using.
Does anyone have this working on the Mac? We are doing cross-platform development so we have some developers working on Windows and others working in OSX. I can’t seem to get the debugger to step into code that is in DLLs on OSX…it just shows the Assembly Browser which doesn’t really allow for breakpoints…
Would you mind letting us know how you got this working in Unity 4? My registry doesn’t contain a Location folder under Unity Editor 4.x, so the build fails. Hard coded the UnityInstallFolder path for now and everything seems to be working great, but that’s just a stop gap.
I should also mention that breakpoints in the DLL work, and stepping into the DLL works, but “Go to declaration” does not – MonoDevelop will still open up the Assembly Browser, even though it ought to be able to open the source file for you.
On that note, if the Assembly Browser is not showing you disassembled source, you need to go to Tools > Options … Preferences > Build, and then on the Assembly Folders tab, add your Unity\Editor\Data\Managed folder.
Also, I have experienced cases where MonoDevelop did not step gracefully through my DLL source code – for example F10 (step over) might advance back out of the DLL code. This seemed to be a temporary MonoDevelop glitch, and it went away after closing down and updating the solution.
Whoever is using VS among Unity might check this asset solution, so you will not ever need to add post build events to your project file by just specifying target output folder.
Wow, i was trying for some days now, i had copiled using monodevelop before but until now i was not able to compile using vs.net and it works on 2012 too.
by the way i think the proper file on unity 4.1 is in 4.5 not in 4.0 folder
Unhandled Exception: System.TypeInitializationException: The type initializer for 'Mono.Cecil.Metadata.TableHeap' threw an exception. ---> System.ArgumentException: Value does not fall within the expected range.
at System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(Array array, RuntimeFieldHandle fldHandle)
at Mono.Cecil.Metadata.TableHeap..cctor()
--- End of inner exception stack trace ---
at Mono.Cecil.PE.ImageReader.ReadTableHeap()
at Mono.Cecil.PE.ImageReader.ReadImage()
at Mono.Cecil.PE.ImageReader.ReadImageFrom(Stream stream)
at Mono.Cecil.ModuleDefinition.ReadModule(Stream stream, ReaderParameters parameters)
at Mono.Cecil.ModuleDefinition.ReadModule(String fileName, ReaderParameters parameters)
at Pdb2Mdb.Driver.Main(String[] args)
This doesn’t seem to be related to the to the project, the same thing happens when I run the command from the command line.
What Unity version are you using? Maybe they changed something in v4.1.2 - which I still didn’t install. Does the 2.0 pdb2mdb.exe support IEnumerators and coroutines?