Resolving Missing Script References in Unity

Recently, I have been moving some of our important and core code out of the Unity Assets directory and instead compiling them in to a .NET dll. When I did this I encountered in to the issue of all the scenes and prefabs that were using MonoBehaviours from those scripts had lost their reference.

hillaryshocked

I found this surprising initially because I thought the references to components were simply using the .NET assembly name of the class with all the namespaces etc, perhaps in combination with something else to be able to track renames of classes. However it turns out that all it basically does is to put a GUID in the script .meta file which it in turn puts in the guid property of the m_Script field of the objects in the .unity scene files – along with a fileID property. The fileID property is always the same for scripts in the Assets directory, but it is different for every class in a dll – I suppose it uses this to narrow down the search for the assembly reference or something like that.

Now this is very little information to work with, so it’s actually harder than it should be to resolve these references. You basically can’t see the name of any script it was using or anything useful besides the GUID. When you look at the scene file you can even see that it has an m_Name and m_EditorClassIdentifier property, but it doesn’t fill them in! This is most likely why, when you look at a missing script in the inspector, it doesn’t even show you the name of it, so you have to rely on your memory to figure out what it was if you are going to fix it by hand – and I don’t know about you, but I certainly can’t do that. TL;DR the format was meticulously designed to break your heart.

ddcry

If you are in total panic right now, and don’t have any way to recover the meta files associated with your old scripts, then anything in this post beyond this paragraph won’t be useful to you. All the advice I can give is that you may be able to look in the scene files at the field names shown for the component and see how the variable names line up with those in your classes. Then you figure out what the new GUID and fileID for those are and edit it in there by hand. That will take quite some time however and I wouldn’t wish it upon my worst enemy. You could probably make some really fancy analysis program for recovery, and someone should maybe do so, but that would take a lot of effort. The best way to not end up in big trouble is to backup your stuff and put your meta files in version control. And also to use the plain text unity scene/prefab format (check the Editor Settings and/or rtfm). Do this, and the gods will smile upon you, or at the very least, I will.

I did look around a bit to see if someone already solved the problem, but pretty much the only “solution” I could find was scripts that would find the missing script references and let you assign them manually, which doesn’t help at all because you can’t get any hint of what the script was named so it’s only slightly better than checking every component manually by clicking on it. But that’s not good enough.

Expectasians-High-Expectations-Asian-Father

I managed to make a solution that does eliminate a large portion of the manual labor. There is still some set up that you do for every missing component, but it’s relatively small. I’ll describe the workflow first, and then tell you how it works internally. Finally I will share the script with you in case you don’t want to write your own.

You start out by figuring out which components possibly could be the ones that are missing. For me this was easy, I knew that the references went missing when I compiled things in to a DLL, so all I really had to do was to search through all of the source files for the DLL for lines containing “MonoBehaviour“. If you don’t know, then one thing you might do is simply to check the scripts from your recently removed meta files. Unity removes these automatically because it hates you so I can’t stress enough how it’s important to have them in version control. Once you’ve done that you can search through the file that the meta file was for in your sources for MonoBehaviours.

Now that you hopefully know which components likely went missing, and have those written down somewhere. You can create a fresh new empty scene, and have it contain nothing but an empty object containing just the MonoBehaviour you are currently fixing.

Save this scene – locate your meta file. Once you have done this, you are set up to run my script to fix one component in your entire project.

What you do is that you pass the script the meta file, as well as the scene you just set up and saved, and also one or more paths that it should search through for scenes and prefabs. It does this recursively so you don’t need to specify every subdirectory of every subdirectory etc.  Passing multiple directories is mainly useful if you have a huge Assets directory and want it to only look through “Assets/Scenes” and “Assets/MyPrefabs” or something like that. I just passed it “Assets” and it was fast enough.

goodenough2txt

What the script will then do is that it will read your meta file, and find the guid that used to be correct. Then it will read your scene file to extract what the new guid is, and also the new fileID. Knowing what the old fileID was is not important, it will simply replace the old one with the new one, it only needs to know what the old guid was to be able to locate it in scenes and prefabs. Once it has the data it asks you if the data seems to make sense to you, and if you answer “y”, then it will start replacing all of the old references for you. Here is a sample invocation of the script, cleaned up a little to remove some of the verbosity:

{ ludus } » racket Pipeline/fix-script-references.rkt ../../../oldludusmetas/CrowdDuplicator.boo.meta Assets/Scenes/FixReferences.unity Assets

I’m going to replace guid 79e4e0dfe8d9bf94b9622e6d68377772 with d79c0ede0316c2b429fad82aa7fc643f and set the file ID to 1803148156, is this ok?(y/n): y
Okay proceeding…
Found old reference(s) in C:\Users\usefulProgrammer\src\boo\ludus\Assets\ExperimentsArchive\drawcalltest\crowd.unity, replacing..
Found old reference(s) in C:\Users\usefulProgrammer\src\boo\ludus\Assets\Scenes\Crowd.unity, replacing..
Found old reference(s) in C:\Users\usefulProgrammer\src\boo\ludus\Assets\Scenes\CrowdPoses.unity, replacing..
<… and so on>

Done!

Once it’s finished you remove the component from the empty object in the scene and add the next component, save and change the meta file to the current relevant one. If you accidentally start mixing meta files etc then you will pretty much be screwed, so be careful with this. Also don’t forget to back up everything before you do this! Cool? Cool.

racketlogo

And that’s really all there is to it, I wrote the script using Racket, which was very pleasant. The script is available here under a CC0 license.

This process could probably be improved somewhat, for example you could script the editor to add all of the components for you and try to figure out which meta file it should look at etc, but that is definitely more difficult to implement and probably rather error prone. If Unity actually used the m_Name field or m_EditorClassIdentifier that they simply left empty – then it would have been less of an undertaking to automate this further. For me, going further than this would stop saving time for us in this situation, which is why I started writing the script in the first place. If we end up having a bigger catastrophy which requires really intense recovery then I’ll let you know what I do about it. But since we are using version control I suspect we won’t need anything more sophisticated than this.

I hope this post helped you out, or at the very least made you put your .meta files in version control because otherwise you could lose everything. See you next time.

fiiiiiiiiine

2 thoughts on “Resolving Missing Script References in Unity”

    1. Hey Lior, thanks for your comment.

      I can try and remember to give your script a try next time I move more stuff in to DLLs, which will happen eventually – or if there is perhaps some reference which I forgot to resolve using my script. Right now though there is nothing to resolve in our project. Though it should be fairly easy to test it yourself, just create a MonoBehaviour, create a scene with an object and attach the behaviour. Now, outside of unity compile the script to a .NET dll, remove the script from Assets and put the dll in Assets. Then you should get to our scenario.

      Cheers!

Leave a Reply

Your email address will not be published.