- Home /
Bizarre Co-routine errors after re-loading scenes
I say 'Bizarre' because until now I've been able to debug my issues relatively competently. The scenario-
When i play my scene normally from the inspector i have no issues at all. All co-routines function as normal. When i reload the level using application.loadlevel or load it from another scene i get the following error on multiple Co-routines (but not all)
NullReferenceException: Object reference not set to an instance of an object
Daniella+<Bounce>c__Iterator13.MoveNext () (at Assets/Resources/GAME2/Scripts/CutScenes/Daniella.cs:108)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
Daniella:RightStep() (at Assets/Resources/GAME2/Scripts/CutScenes/Daniella.cs:91)
<OM5Steps>c__Iterator34:MoveNext() (at Assets/Resources/GAME2/Scripts/Handlers/PlayerHandler.cs:595)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
PlayerHandler:CSONE() (at Assets/Resources/GAME2/Scripts/Handlers/PlayerHandler.cs:153)
PlayerHandler:OnLevelWasLoaded(Int32) (at Assets/Resources/GAME2/Scripts/Handlers/PlayerHandler.cs:1046)
I've included the full log as opposed to just the null reference exception because I'm absolutely lost as to what's being called as null. The co-routine itself is part of a movement script, it bounces the rigidbody up and down of an NPC whilst it moves during a cut scene.
It plays 3 times successfully in spite of the error and then fails to perform the last movement step. Here's the relevant code snippets mentioned in the log, I'm listing them all because i can't isolate the problem to any single part of it, the script on the NPC is IDENTICAL to several other NPC's performing the IDENTICAL co-routine in the scene without any errors.
Daniella.cs:108
IEnumerator Bounce()
{
rb.velocity = new Vector3(0, 1, 0); //line 108
yield return new WaitForSeconds(0.2f);
rb.velocity = new Vector3(0, 0, 0);
}
Daniella.cs:91
public void RightStep()
{
pos += Vector3.right;
SoundHandler.instance.RandomizeEnemSfx(Move1, Move2);
StartCoroutine(Bounce()); // line 91
}
PlayerHandler (This script isn't destroyed on load) cs:595
IEnumerator OM5Steps()
{
danScript.RightStep(); //line 595
yield return new WaitForSeconds(0.35f);
danScript.RightStep();
orphanScript.RightStep();
yield return new WaitForSeconds(0.35f);
danScript.RightStep();
orphanScript.RightStep();
yield return new WaitForSeconds(0.35f);
danScript.RightStep(); // this last step is never executed
orphanScript.RightStep();
yield return new WaitForSeconds(0.35f);
orphanScript.RightStep();
yield return new WaitForSeconds(0.35f);
orphanScript.RightStep();
yield return new WaitForSeconds(0.35f);
CSONE(); // this line is executed successfully in spite of the error
}
Playerhandler cs:153 is simply just starting the above co-routine in a method called CSONE()
and lastly Playerhandler cs:1046, it exists within an OnLevelWasLoaded method and simply calls the method that starts the co-routines off.
if(MainScene == -1)
{
DaniellaF = GameObject.Find("DaniellaF");
DoogerF = GameObject.Find("DoogerF");
OrphanF = GameObject.Find("OrphanMaster");
CSONE(); // line 1046
Debug.Log ("first time here, playing opening scene");
}
I'm terribly sorry this is so long, if i had any idea where the error was i'd post just that but i really have no clue. I've never had a null ref error like this one. I've tried looking to see if the rigidbody wasn't being properly assigned but it shows up in the inspector fine after the script uses a .getComponent call and the co-routine plays the first three times fine. Thing is i get the error right at the START of the scene, it gets called successfully after the error shows up two or three times but then not for the last step, which isn't called any differently.
I have identical scripts running with no issue, im so confused :s this is literally game-breaking, if you need anymore information or need to screenshare, i'm up for trying anything at this point. Reverted to backups, error still persists. :( take pity on a poor soul. I get the same error on the player script a bit later on but im hoping if i can solve this one i'll be able to solve the other.
Did you destroy your rb reference somewhere? I think the rb reference might be destroyed after re-loading and it was trying to fetch the old reference.
You can do a null check on your rb and re assign the rb value again if it is a null value to see whether my guessing is correct or not.
After an hour of adding a null check and debug.log before every possible place that script could be even tangentially talked to or referenced i found for some reason the rb was actually in fact co$$anonymous$$g up as null for some reason. I always overthink my simple problems :')
Thankyou, I honestly think i'd just been staring at the screen for too long. I still have no idea WHY it's beco$$anonymous$$g null, the script isnt marked as dont destroy on load so it should be running its start method and assigning it there when the scene starts :') for some reason the co-routing might be starting before the start method is called somehow? Hell if i know but it's fixed, thankyou very much :P
Answer by I_CAST_FIST · Sep 17, 2015 at 04:39 PM
Fixed it after reading sys12's comment, according to my debug.logs the co-routine is somehow being called before the start method of the script was called.
The script isn't marked as Don'tDestroyOnLoad and i have no idea how that's even possible but adding a simple null check here in the co-routine in question-
if(rb != null)
{
rb.velocity = new Vector3(0, 1, 0);
yield return new WaitForSeconds(0.2f);
rb.velocity = new Vector3(0, 0, 0);
}
else if (rb == null)
{
Debug.Log("rb was null in Bounce! Reassigning");
rb = gameObject.GetComponent<Rigidbody2D>();
}
Seemed to fix the problem. I think this is a simple lesson to use proper null-checking more often and get more sleep :') Thankyou both @sys12 and @Bunny83 very much, If sys had put it as an answer instead of a question i would've marked that as the solution, for now this is what fixed it :P
I would suggest you to do something like this:
rb = rb??gameObject.GetComponent<Rigidbody2D>();
rb.velocity = new Vector3(0, 1, 0);
yield return new WaitForSeconds(0.2f);
rb.velocity = new Vector3(0, 0, 0);
That's much cleaner, thankyou :') Suffice to say i have serious optimization to do before this is complete haha
As a heads up don't use the null-coalescing operator (??) with Unity objects. This will lead to you problems that will be very confusing and hard to debug. Unity has overloaded the == operator which interacts with C++. Every Unity Object has a C++ side and a C# side. Unity's == operator checks to see if the C++ side is alive or not. Since you can't override the null-coalescing operator, Unity can't do this check. This will end is cases where the object is not null and and while debugging it has an instance but the moment you go to use it you will get a null reference error.
You will sometimes see the code below which at first glance looks like it could never be true. but for the reason above it can be true (the C++ side has been disposed but the C# is alive) -
if(this == null)
{
return;
}
Cheers,
Answer by Bunny83 · Sep 17, 2015 at 03:45 PM
I guess that the object where your coroutine runs on isn't destroyed when you load the new scene. Did you use DontDestroyOnLoad on it? If you do that means the object and it's coroutine will keep running once your switched the scenes. The coroutine seem to use variables which most likely references other objects in the scene. Those objects probably have been destroyed when you loaded the new scene.
Therefore you get a nullreference exception when the coroutine tries to access the object.