When Your Unity Component Mysteriously Disappeared
Created on: 13 Dec 21 01:28 +0700 by Son Nguyen Hoang in English
A Short Analysis on an interesting bug!
Recently, I encountered a tricky, mysterious bug occurring in my company’s game project. “That’s just an animation bug” so I thought, in fact, the bug is far more trickier than it should have been. Thus, I wrote down this small article to explain the problem and hope every Unity developers be aware of it.
Background
It is a must to have some assumptions and background information on the issue I encountered. Subject: The object where the bug occurs is a normal game object, which has many child objects inside of it. For now, we refer to it as Object A
Problems: Some child objects of Object A free fall whenever the parent prefab be spawned.
- Weirdly enough, this behavior only occurs after the game has been built (exported) into the APK file. The bug happens on emulators (or any mobile phone devices) but works perfectly normal in Unity Editor.
- Also, note that Object A was first imported into the client as an Asset Bundle. When the main game started, it is dynamically loaded and spawned into the game scene.
- Object A was first created by an artist working on a separate project (Project B).
Given these information, I started to delve deep into the source code and tweak it around to see how everything works.
Start Investigating
My first action is to create a different scene where every other redundant or unrelated to Object A to be removed. Thus, I built a separate APK game file just to check Object A’s behavior on the emulator app.
Then, the next assumption I made is that it could have been that some reference inside the game object had been lost after the build. Therefore, I did a simple check by looping through every child in Object A, then using the method Component.GetComponents
to list all attached scripts on each child. After that, these results were compared to the original prefab before it was compressed into Asset Bundle. The result is clear and gives me hope: Some children of Object A of the build version lack script HingeJoint2d and SpringJoint2d attached to it. They are also the ones that free fall when the object appears.
But what makes these scripts vanish?
When things get even weirder?
To continue the investigation, I first make sure two things:
- Object A when first be crafted by the artists is bug-free. I tested this by building a separate scene with only it then exported an APK file directly from Project B. The result is that Object A works perfectly normal on mobile & emulators.
- The script to build Object A into Assets Bundle must be bug-free as well. Indeed, all methods used in the process were built upon the Unity library. No bug should occur at this stage.
Confusing enough, with the information that’s the two scripts HingeJoint2d and SpringJoin2d had been missing, I tried to locate the exact child object to which the scripts belong and get the reference of HingeJoin2d/SpringJoin2d from it. The script was very straightforward.
var targetedChildObject = GameObject.Find(“ChildObjectName”); //
var hingeJoin = targetedChildObject.GetComponent<HingeJoin2d>();
var sprintjoin = targetedChildObject.GetComponent< SpringJoin2d >();
// More debug code to log out information in build version of the game
Surprisingly, now the bug had been resolved! Funnier is the fact that I don’t even need to return the reference, simply calling GetComponent is enough already. So the below code makes the bug gone as well.
var targetedChildObject = GameObject.Find(“ChildObjectName”);
targetedChildObject.GetComponent<HingeJoin2d>();
targetedChildObject.GetComponent<SpringJoin2d >();
What the hell is happening here? As you can see, I have not done anything to actual fixing the bug, I simply debug all relevant information to do further investigations. So, how the hell the two mostly neutral, irrelevant lines of codes using GetComponent<T>
can solve the issue? Is there any mystery behind the method GetComponent<T>
If yes, can I look for it? Unfortunately, the method itself belongs to the Unity library, and the actual code is not published.
Is it a tricky error from the Engine producer? Or is it a joke from the engine developer? Am I crazy? Is there anyone who encountered the same bug as I did? The answer is no. I tried to search for similar cases but no developer yet to be found encountered the same bug as mine.
Should I make a hotfix then remove this task from my Jira? Should I let it go as a glitch in the matrix, never to be understood, and sometimes come back to haunt our dreams?
The answer is, yet again, a big no.
Explanation
The bullet point is this tiny, cute yet shockingly simple checkbox: Strip Engine Code. What it can do is crucial to understanding this mystery, according to our friendly tutorial provided from the Unity Corp itself, the job of Code Stripping is simply to:
Code stripping in Unity Code size has a direct influence on disk space and runtime memory. It’s important that Unity removes any code paths you aren’t using from the code base. Unity strips code automatically […]
This sentence beautifully suggests two points, the first point is that library codes that have never been used inside the project will be discarded if this checkbox is selected; next point is that it’s very likely that the engine itself had selected HingeJoin2d/SpringJoin2d to be removed in build version. To verify this assumption, we untick the box and rebuild the game. This time the object had been fixed!
However, this doesn’t explain why HingeJoin2d/SpringJoin2d are chosen as targets to be removed in the game. We have it on Object A, don’t we? Unfortunately, in the main project, there is no Object A in the assets folder but only the Asset Bundle of Object A, in which the original prefab be compressed then transformed into a different format. This asset bundle then be loaded dynamically in the game. Hence, the engine failed to detect the usage of HingeJoint2d/SpringJoint2d in the project itself.
Next, a quick check on the whole project reveals that there is no other reference of these two scripts outside of Object A. This also explains why when the code using GetComponent<T>
be added, the bug suddenly gone! Because these two line of code contains reference to HingeJoin2d/SpringJoin2d, thus allow them to be complied in the final APK file.
Finale
This interesting bug could be the best example of the type of error that’s so difficult to track but extremely fast to patch. The next step is simply to add two lines of code in which there are references to HingeJoint2d and SpringJoint2d class in the main project. You can put the two in any place but remember to put some lines of comment for the next people who come after you! Be proud that you may save someone from a big headache.
Reference:
The version of Unity when the bug is found in Unity 2019.4.29f1 (Android).
https://learn.unity.com/tutorial/memory-management-in-unity#5c7f8528edbc2a002053b59a
Images were collected from https://unsplash.com/